From ff144d3758b4170211564283b9271bb908d9cdcf Mon Sep 17 00:00:00 2001 From: bekzod Date: Tue, 13 Jun 2017 01:50:59 +0500 Subject: [PATCH 1/3] add error stack to `.later` --- lib/backburner/binary-search.ts | 10 +++--- lib/index.ts | 56 ++++++++++++++------------------- 2 files changed, 28 insertions(+), 38 deletions(-) diff --git a/lib/backburner/binary-search.ts b/lib/backburner/binary-search.ts index 4807f439..754c677d 100644 --- a/lib/backburner/binary-search.ts +++ b/lib/backburner/binary-search.ts @@ -1,24 +1,24 @@ export default function binarySearch(time, timers) { let start = 0; - let end = timers.length - 2; + let end = timers.length - 5; let middle; let l; while (start < end) { // since timers is an array of pairs 'l' will always // be an integer - l = (end - start) / 2; + l = (end - start) / 5; // compensate for the index in case even number // of pairs inside timers - middle = start + l - (l % 2); + middle = start + l - (l % 5); if (time >= timers[middle]) { - start = middle + 2; + start = middle + 5; } else { end = middle; } } - return (time >= timers[start]) ? start + 2 : start; + return (time >= timers[start]) ? start + 5 : start; } diff --git a/lib/index.ts b/lib/index.ts index ee46d955..a01d3276 100644 --- a/lib/index.ts +++ b/lib/index.ts @@ -43,6 +43,8 @@ function parseArgs() { return [target, method, args]; } +let UUID = 0; + export default class Backburner { public static Queue = Queue; @@ -327,25 +329,7 @@ export default class Backburner { } } - let onError = getOnError(this.options); - let executeAt = this._platform.now() + wait; - - let fn; - if (onError) { - fn = function() { - try { - method.apply(target, args); - } catch (e) { - onError(e); - } - }; - } else { - fn = function() { - method.apply(target, args); - }; - } - - return this._setTimeout(fn, executeAt); + return this._setTimeout(target, method, args, wait); } public throttle(target: T, methodName: keyof T, wait?: number | string, immediate?: boolean): Timer; @@ -530,9 +514,9 @@ export default class Backburner { if (!timer) { return false; } let timerType = typeof timer; - if (timerType === 'number' || timerType === 'string') { // we're cancelling a throttle or debounce + if (timerType === 'number') { // we're cancelling a throttle or debounce return this._cancelItem(timer, this._throttlers) || this._cancelItem(timer, this._debouncees); - } else if (timerType === 'function') { // we're cancelling a setTimeout + } else if (timerType === 'string') { // we're cancelling a setTimeout return this._cancelLaterTimer(timer); } else if (timerType === 'object' && timer.queue && timer.method) { // we're cancelling a deferOnce return timer.queue.cancel(timer); @@ -586,31 +570,33 @@ export default class Backburner { } } - private _setTimeout(fn, executeAt) { + private _setTimeout(target, method, args, wait) { + let executeAt = this._platform.now() + wait; + let id = (UUID++) + ''; + if (this._timers.length === 0) { - this._timers.push(executeAt, fn); + this._timers.push(executeAt, id, target, method, args); this._installTimerTimeout(); - return fn; + return id; } // find position to insert let i = searchTimer(executeAt, this._timers); - - this._timers.splice(i, 0, executeAt, fn); + this._timers.splice(i, 0, executeAt, id, target, method, args); // we should be the new earliest timer if i == 0 if (i === 0) { this._reinstallTimerTimeout(); } - return fn; + return id; } private _cancelLaterTimer(timer) { - for (let i = 1; i < this._timers.length; i += 2) { + for (let i = 1; i < this._timers.length; i += 5) { if (this._timers[i] === timer) { i = i - 1; - this._timers.splice(i, 2); // remove the two elements + this._timers.splice(i, 5); if (i === 0) { this._reinstallTimerTimeout(); } @@ -662,15 +648,19 @@ export default class Backburner { private _scheduleExpiredTimers() { let timers = this._timers; - let l = timers.length; let i = 0; + let l = timers.length; let defaultQueue = this.options.defaultQueue; let n = this._platform.now(); - for (; i < l; i += 2) { + + for (; i < l; i += 5) { let executeAt = timers[i]; if (executeAt <= n) { - let fn = timers[i + 1]; - this.schedule(defaultQueue, null, fn); + let target = timers[i + 2]; + let method = timers[i + 3]; + let args = timers[i + 4]; + let stack = this.DEBUG ? new Error() : undefined; + this.currentInstance.schedule(defaultQueue, target, method, args, false, stack); } else { break; } From f68845e232774d176ea8789b0e56d1649db0b760 Mon Sep 17 00:00:00 2001 From: Robert Jackson Date: Sat, 20 Jan 2018 16:58:53 -0500 Subject: [PATCH 2/3] Fix type issue. --- lib/index.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/index.ts b/lib/index.ts index a01d3276..2ad7c7f3 100644 --- a/lib/index.ts +++ b/lib/index.ts @@ -660,7 +660,7 @@ export default class Backburner { let method = timers[i + 3]; let args = timers[i + 4]; let stack = this.DEBUG ? new Error() : undefined; - this.currentInstance.schedule(defaultQueue, target, method, args, false, stack); + this.currentInstance!.schedule(defaultQueue, target, method, args, false, stack); } else { break; } From c50165b595a10fce3f894caaa87b3803ae4bf21a Mon Sep 17 00:00:00 2001 From: Robert Jackson Date: Sat, 20 Jan 2018 17:25:28 -0500 Subject: [PATCH 3/3] Move stack capture into `_setTimeout`. Add tests ensuring stack capture works properly. --- lib/backburner/binary-search.ts | 10 ++++----- lib/index.ts | 13 ++++++------ tests/debug-test.ts | 37 +++++++++++++++++++++++++++++++-- 3 files changed, 47 insertions(+), 13 deletions(-) diff --git a/lib/backburner/binary-search.ts b/lib/backburner/binary-search.ts index 754c677d..98053b9d 100644 --- a/lib/backburner/binary-search.ts +++ b/lib/backburner/binary-search.ts @@ -1,24 +1,24 @@ export default function binarySearch(time, timers) { let start = 0; - let end = timers.length - 5; + let end = timers.length - 6; let middle; let l; while (start < end) { // since timers is an array of pairs 'l' will always // be an integer - l = (end - start) / 5; + l = (end - start) / 6; // compensate for the index in case even number // of pairs inside timers - middle = start + l - (l % 5); + middle = start + l - (l % 6); if (time >= timers[middle]) { - start = middle + 5; + start = middle + 6; } else { end = middle; } } - return (time >= timers[start]) ? start + 5 : start; + return (time >= timers[start]) ? start + 6 : start; } diff --git a/lib/index.ts b/lib/index.ts index 2ad7c7f3..641119a8 100644 --- a/lib/index.ts +++ b/lib/index.ts @@ -571,18 +571,19 @@ export default class Backburner { } private _setTimeout(target, method, args, wait) { + let stack = this.DEBUG ? new Error() : undefined; let executeAt = this._platform.now() + wait; let id = (UUID++) + ''; if (this._timers.length === 0) { - this._timers.push(executeAt, id, target, method, args); + this._timers.push(executeAt, id, target, method, args, stack); this._installTimerTimeout(); return id; } // find position to insert let i = searchTimer(executeAt, this._timers); - this._timers.splice(i, 0, executeAt, id, target, method, args); + this._timers.splice(i, 0, executeAt, id, target, method, args, stack); // we should be the new earliest timer if i == 0 if (i === 0) { @@ -593,10 +594,10 @@ export default class Backburner { } private _cancelLaterTimer(timer) { - for (let i = 1; i < this._timers.length; i += 5) { + for (let i = 1; i < this._timers.length; i += 6) { if (this._timers[i] === timer) { i = i - 1; - this._timers.splice(i, 5); + this._timers.splice(i, 6); if (i === 0) { this._reinstallTimerTimeout(); } @@ -653,13 +654,13 @@ export default class Backburner { let defaultQueue = this.options.defaultQueue; let n = this._platform.now(); - for (; i < l; i += 5) { + for (; i < l; i += 6) { let executeAt = timers[i]; if (executeAt <= n) { let target = timers[i + 2]; let method = timers[i + 3]; let args = timers[i + 4]; - let stack = this.DEBUG ? new Error() : undefined; + let stack = timers[i + 5]; this.currentInstance!.schedule(defaultQueue, target, method, args, false, stack); } else { break; diff --git a/tests/debug-test.ts b/tests/debug-test.ts index 06860f47..b53a6fb5 100644 --- a/tests/debug-test.ts +++ b/tests/debug-test.ts @@ -2,7 +2,7 @@ import Backburner from 'backburner'; QUnit.module('tests/debug'); -QUnit.test('DEBUG flag enables stack tagging', function(assert) { +QUnit.test('schedule - DEBUG flag enables stack tagging', function(assert) { let bb = new Backburner(['one']); bb.schedule('one', () => {}); @@ -26,7 +26,7 @@ QUnit.test('DEBUG flag enables stack tagging', function(assert) { assert.ok(errorRecordedForStack.stack, 'stack is recorded'); }; - bb = new Backburner(['errors'], {onError: onError}); + bb = new Backburner(['errors'], { onError }); bb.DEBUG = true; bb.run(() => { @@ -36,3 +36,36 @@ QUnit.test('DEBUG flag enables stack tagging', function(assert) { }); } }); + +QUnit.test('later - DEBUG flag off does not capture stack', function(assert) { + let done = assert.async(); + let onError = function(error, errorRecordedForStack) { + assert.strictEqual(errorRecordedForStack, undefined, 'errorRecordedForStack is not passed to error function when DEBUG is not set'); + done(); + }; + let bb = new Backburner(['one'], { onError }); + + bb.later(() => { + throw new Error('message!'); + }); +}); + +if (new Error().stack) { // workaround for CLI runner :( + QUnit.test('later - DEBUG flag on captures stack', function(assert) { + assert.expect(3); + + let done = assert.async(); + let onError = function(error, errorRecordedForStack) { + assert.ok(errorRecordedForStack, 'errorRecordedForStack passed to error function'); + assert.ok(errorRecordedForStack.stack, 'stack is recorded'); + assert.ok(errorRecordedForStack.stack.indexOf('later') > -1, 'stack includes `later` invocation'); + done(); + }; + let bb = new Backburner(['one'], { onError }); + bb.DEBUG = true; + + bb.later(() => { + throw new Error('message!'); + }); + }); +}