Skip to content

Commit

Permalink
Use set and clear debouncer upon completion. Fixes #5250.
Browse files Browse the repository at this point in the history
  • Loading branch information
kevinpschaaf committed Feb 27, 2019
1 parent 187f10a commit e8c24ff
Show file tree
Hide file tree
Showing 3 changed files with 86 additions and 26 deletions.
36 changes: 36 additions & 0 deletions lib/utils/debounce.js
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ export class Debouncer {
if (this.isActive()) {
this._asyncModule.cancel(/** @type {number} */(this._timer));
this._timer = null;
debouncerQueue.delete(this);
}
}
/**
Expand Down Expand Up @@ -112,3 +113,38 @@ export class Debouncer {
return debouncer;
}
}

let debouncerQueue = new Set();

/**
* Adds a `Debouncer` to a list of globally flushable tasks.
*
* @param {!Debouncer} debouncer Debouncer to enqueue
* @return {void}
*/
export const enqueueDebouncer = function(debouncer) {
if (debouncerQueue.has(debouncer)) {
debouncerQueue.delete(debouncer);
}
debouncerQueue.add(debouncer);
};

/**
* Flushes any enqueued debouncers
*
* @return {void}
*/
export const flushDebouncers = function() {
const didFlush = Boolean(debouncerQueue.size);
debouncerQueue.forEach(debouncer => {
try {
debouncer.flush();
} catch(e) {
setTimeout(() => {
throw e;
});
}
});
debouncerQueue = new Set();
return didFlush;
};
28 changes: 2 additions & 26 deletions lib/utils/flush.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,32 +11,8 @@ import './boot.js';
/* eslint-disable no-unused-vars */
import { Debouncer } from '../utils/debounce.js'; // used in type annotations
/* eslint-enable no-unused-vars */

let debouncerQueue = [];

/**
* Adds a `Debouncer` to a list of globally flushable tasks.
*
* @param {!Debouncer} debouncer Debouncer to enqueue
* @return {void}
*/
export const enqueueDebouncer = function(debouncer) {
debouncerQueue.push(debouncer);
};

function flushDebouncers() {
const didFlush = Boolean(debouncerQueue.length);
while (debouncerQueue.length) {
try {
debouncerQueue.shift().flush();
} catch(e) {
setTimeout(() => {
throw e;
});
}
}
return didFlush;
}
import { flushDebouncers } from '../utils/debounce.js'; // used in type annotations
export { enqueueDebouncer } from '../utils/debounce.js'; // used in type annotations

/**
* Forces several classes of asynchronously queued tasks to flush:
Expand Down
48 changes: 48 additions & 0 deletions test/unit/debounce.html
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@
import { Polymer } from '../../polymer-legacy.js';
import { Debouncer } from '../../lib/utils/debounce.js';
import { microTask, timeOut, animationFrame, idlePeriod } from '../../lib/utils/async.js';
import { enqueueDebouncer, flush } from '../../lib/utils/flush.js';
Polymer({is: 'x-basic'});

suite('debounce', function() {
Expand Down Expand Up @@ -209,6 +210,53 @@
});

});

suite('enqueueDebouncer & flush', function() {
function testEnqueue(shouldFlush, done) {
// Longer-running debouncer
const timeoutCallback = sinon.spy(() => actualCallbacks.push(timeoutCallback));
enqueueDebouncer(Debouncer.debounce(null, timeOut, timeoutCallback));
// Set of short-running debouncers enqueued in the middle of first set
const nestedCallbacks = new Array(150).fill().map((_, i) => sinon.spy(() =>
actualCallbacks.push(nestedCallbacks[i])));
// First set of short-running debouncers
const microtaskCallbacks = new Array(150).fill().map((_, i) => sinon.spy(() => {
actualCallbacks.push(microtaskCallbacks[i]);
if (i === 125) {
nestedCallbacks.forEach(cb =>
enqueueDebouncer(Debouncer.debounce(null, microTask, cb)));
}
}));
microtaskCallbacks.forEach(cb =>
enqueueDebouncer(Debouncer.debounce(null, microTask, cb)));
// Expect short before long
let expectedCallbacks;
const actualCallbacks = [];
const verify = () => {
actualCallbacks.forEach(cb => assert.isTrue(cb.calledOnce));
assert.deepEqual(expectedCallbacks, actualCallbacks);
done();
};
if (shouldFlush) {
expectedCallbacks = [timeoutCallback, ...microtaskCallbacks, ...nestedCallbacks];
flush();
// When flushing, order is order of enqueing
verify();
} else {
expectedCallbacks = [...microtaskCallbacks, ...nestedCallbacks, timeoutCallback];
timeOut.run(verify);
}
}

test('non-flushed', function(done) {
testEnqueue(false, done);
});

test('flushed', function(done) {
testEnqueue(true, done);
});

});
});
</script>
</body>
Expand Down

0 comments on commit e8c24ff

Please sign in to comment.