Skip to content

Commit 3e4b356

Browse files
authored
Merge pull request #319 from bekzod/debounce-throttle-fix
avoid creating runloop for debounce and throttle
2 parents 43c153d + 4144706 commit 3e4b356

File tree

3 files changed

+54
-84
lines changed

3 files changed

+54
-84
lines changed

lib/backburner/utils.ts

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -22,12 +22,12 @@ export function findItem(target, method, collection) {
2222
return index;
2323
}
2424

25-
export function findTimer(timer, collection) {
25+
export function findTimerItem(target, method, collection) {
2626
let index = -1;
2727

28-
for (let i = 3; i < collection.length; i += 4) {
29-
if (collection[i] === timer) {
30-
index = i - 3;
28+
for (let i = 2, l = collection.length; i < l; i += 6) {
29+
if (collection[i] === target && collection[i + 1] === method) {
30+
index = i - 2;
3131
break;
3232
}
3333
}

lib/index.ts

Lines changed: 49 additions & 80 deletions
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,7 @@ import {
88
IPlatform,
99
} from './backburner/platform';
1010
import {
11-
findItem,
12-
findTimer,
11+
findTimerItem,
1312
getOnError,
1413
isCoercableNumber
1514
} from './backburner/utils';
@@ -24,6 +23,9 @@ type Timer = string | number;
2423

2524
const noop = function() {};
2625

26+
const SET_TIMEOUT = setTimeout;
27+
const DISABLE_SCHEDULE = Object.freeze([]);
28+
2729
function parseArgs(...args: any[]);
2830
function parseArgs() {
2931
let length = arguments.length;
@@ -193,8 +195,6 @@ export default class Backburner {
193195
private _onEnd: (currentInstance: DeferredActionQueues, nextInstance: DeferredActionQueues | null) => void;
194196
private queueNames: string[];
195197
private instanceStack: DeferredActionQueues[] = [];
196-
private _debouncees: any[] = [];
197-
private _throttlers: any[] = [];
198198
private _eventCallbacks: {
199199
end: Function[];
200200
begin: Function[];
@@ -450,27 +450,23 @@ export default class Backburner {
450450
throttleCount++;
451451
let [target, method, args, wait, isImmediate = true] = parseDebounceArgs(...arguments);
452452

453-
let index = findItem(target, method, this._throttlers);
454-
if (index > -1) {
455-
this._throttlers[index + 2] = args;
456-
return this._throttlers[index + 3];
457-
} // throttled
458-
459-
let timer = this._platform.setTimeout(() => {
460-
let i = findTimer(timer, this._throttlers);
461-
let [context, func, params] = this._throttlers.splice(i, 4);
462-
if (isImmediate === false) {
463-
this._run(context, func, params);
464-
}
465-
}, wait);
453+
let index = findTimerItem(target, method, this._timers);
454+
let timerId;
455+
if (index === -1) {
456+
timerId = this._later(target, method, isImmediate ? DISABLE_SCHEDULE : args, wait);
466457

467-
if (isImmediate) {
468-
this._join(target, method, args);
458+
if (isImmediate) {
459+
this._join(target, method, args);
460+
}
461+
} else {
462+
timerId = this._timers[index + 1];
463+
let argIndex = index + 4;
464+
if (this._timers[argIndex] !== DISABLE_SCHEDULE) {
465+
this._timers[argIndex] = args;
466+
}
469467
}
470468

471-
this._throttlers.push(target, method, args, timer);
472-
473-
return timer;
469+
return timerId;
474470
}
475471

476472
// with target, with method name, with optional immediate
@@ -494,65 +490,49 @@ export default class Backburner {
494490
debounceCount++;
495491
let [target, method, args, wait, isImmediate = false] = parseDebounceArgs(...arguments);
496492

497-
// Remove debouncee
498-
let index = findItem(target, method, this._debouncees);
499-
if (index > -1) {
500-
let timerId = this._debouncees[index + 3];
501-
this._platform.clearTimeout(timerId);
502-
this._debouncees.splice(index, 4);
503-
}
493+
let index = findTimerItem(target, method, this._timers);
504494

505-
let timer = this._platform.setTimeout(() => {
506-
let i = findTimer(timer, this._debouncees);
507-
let [context, func, params] = this._debouncees.splice(i, 4);
508-
if (isImmediate === false) {
509-
this._run(context, func, params);
495+
let timerId;
496+
if (index === -1) {
497+
timerId = this._later(target, method, isImmediate ? DISABLE_SCHEDULE : args, wait);
498+
if (isImmediate) {
499+
this._join(target, method, args);
510500
}
511-
}, wait);
501+
} else {
502+
let executeAt = this._platform.now() + wait || this._timers[index];
503+
this._timers[index] = executeAt;
512504

513-
if (isImmediate && index === -1) {
514-
this._join(target, method, args);
515-
}
505+
let argIndex = index + 4;
506+
if (this._timers[argIndex] !== DISABLE_SCHEDULE) {
507+
this._timers[argIndex] = args;
508+
}
509+
timerId = this._timers[index + 1];
516510

517-
this._debouncees.push(target, method, args, timer);
511+
if (index === 0) {
512+
this._reinstallTimerTimeout();
513+
}
514+
}
518515

519-
return timer;
516+
return timerId;
520517
}
521518

522519
public cancelTimers() {
523520
cancelTimersCount++;
524-
for (let i = 3; i < this._throttlers.length; i += 4) {
525-
this._platform.clearTimeout(this._throttlers[i]);
526-
}
527-
this._throttlers = [];
528-
529-
for (let t = 3; t < this._debouncees.length; t += 4) {
530-
this._platform.clearTimeout(this._debouncees[t]);
531-
}
532-
this._debouncees = [];
533-
534521
this._clearTimerTimeout();
535522
this._timers = [];
536-
537523
this._cancelAutorun();
538524
}
539525

540526
public hasTimers() {
541-
return this._timers.length > 0 ||
542-
this._debouncees.length > 0 ||
543-
this._throttlers.length > 0 ||
544-
this._autorun !== null;
527+
return this._timers.length > 0 || this._autorun !== null;
545528
}
546529

547530
public cancel(timer?) {
548531
cancelCount++;
549-
550-
if (timer === undefined || timer === null) { return false; }
551-
532+
if (timer === null || timer === undefined) { return false; }
552533
let timerType = typeof timer;
553-
if (timerType === 'number') { // we're cancelling a throttle or debounce
554-
return this._cancelItem(timer, this._throttlers) || this._cancelItem(timer, this._debouncees);
555-
} else if (timerType === 'string') { // we're cancelling a setTimeout
534+
535+
if (timerType === 'number') { // we're cancelling a setTimeout or throttle or debounce
556536
return this._cancelLaterTimer(timer);
557537
} else if (timerType === 'object' && timer.queue && timer.method) { // we're cancelling a deferOnce
558538
return timer.queue.cancel(timer);
@@ -643,7 +623,7 @@ export default class Backburner {
643623
private _later(target, method, args, wait) {
644624
let stack = this.DEBUG ? new Error() : undefined;
645625
let executeAt = this._platform.now() + wait;
646-
let id = (UUID++) + '';
626+
let id = UUID++;
647627

648628
if (this._timers.length === 0) {
649629
this._timers.push(executeAt, id, target, method, args, stack);
@@ -664,8 +644,7 @@ export default class Backburner {
664644
private _cancelLaterTimer(timer) {
665645
for (let i = 1; i < this._timers.length; i += 6) {
666646
if (this._timers[i] === timer) {
667-
i = i - 1;
668-
this._timers.splice(i, 6);
647+
this._timers.splice(i - 1, 6);
669648
if (i === 0) {
670649
this._reinstallTimerTimeout();
671650
}
@@ -675,17 +654,6 @@ export default class Backburner {
675654
return false;
676655
}
677656

678-
private _cancelItem(timer, array) {
679-
let index = findTimer(timer, array);
680-
681-
if (index > -1) {
682-
this._platform.clearTimeout(timer);
683-
array.splice(index, 4);
684-
return true;
685-
}
686-
return false;
687-
}
688-
689657
/**
690658
Trigger an event. Supports up to two arguments. Designed around
691659
triggering transition events from one run loop instance to the
@@ -726,12 +694,13 @@ export default class Backburner {
726694
for (; i < l; i += 6) {
727695
let executeAt = timers[i];
728696
if (executeAt > n) { break; }
729-
730-
let target = timers[i + 2];
731-
let method = timers[i + 3];
732697
let args = timers[i + 4];
733-
let stack = timers[i + 5];
734-
this.currentInstance!.schedule(defaultQueue, target, method, args, false, stack);
698+
if (args !== DISABLE_SCHEDULE) {
699+
let target = timers[i + 2];
700+
let method = timers[i + 3];
701+
let stack = timers[i + 5];
702+
this.currentInstance!.schedule(defaultQueue, target, method, args, false, stack);
703+
}
735704
}
736705

737706
timers.splice(0, i);

tests/bb-has-timers-test.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ QUnit.test('hasTimers', function(assert) {
1717
assert.ok(bb.hasTimers(), 'hasTimers checks timers');
1818

1919
bb.cancel(timer);
20+
2021
assert.ok(!bb.hasTimers(), 'Timers are cleared');
2122

2223
timer = bb.debounce(target, 'fn', 200);

0 commit comments

Comments
 (0)