diff --git a/lib/timers.js b/lib/timers.js index 574fb810d6..f49227f020 100644 --- a/lib/timers.js +++ b/lib/timers.js @@ -94,8 +94,8 @@ const TIMEOUT_MAX = 2147483647; // 2^31-1 // // - key = time in milliseconds // - value = linked list -const refedLists = {}; -const unrefedLists = {}; +const refedLists = Object.create(null); +const unrefedLists = Object.create(null); // Schedule or re-schedule a timer. @@ -128,23 +128,28 @@ function insert(item, unrefed) { var list = lists[msecs]; if (!list) { debug('no %d list was found in insert, creating a new one', msecs); - // Make a new linked list of timers, and create a TimerWrap to schedule - // processing for the list. - list = new TimersList(msecs, unrefed); - L.init(list); - list._timer._list = list; - - if (unrefed === true) list._timer.unref(); - list._timer.start(msecs); - - lists[msecs] = list; - list._timer[kOnTimeout] = listOnTimeout; + lists[msecs] = list = createTimersList(msecs, unrefed); } L.append(list, item); assert(!L.isEmpty(list)); // list is not empty } +function createTimersList(msecs, unrefed) { + // Make a new linked list of timers, and create a TimerWrap to schedule + // processing for the list. + const list = new TimersList(msecs, unrefed); + L.init(list); + list._timer._list = list; + + if (unrefed === true) list._timer.unref(); + list._timer.start(msecs); + + list._timer[kOnTimeout] = listOnTimeout; + + return list; +} + function TimersList(msecs, unrefed) { this._idleNext = null; // Create the list with the linkedlist properties to this._idlePrev = null; // prevent any unnecessary hidden class changes. @@ -229,7 +234,7 @@ function tryOnTimeout(timer, list) { timer._called = true; var threw = true; try { - timer._onTimeout(); + ontimeout(timer); threw = false; } finally { if (!threw) return; @@ -317,51 +322,76 @@ exports.enroll = function(item, msecs) { */ -exports.setTimeout = function(callback, after) { +exports.setTimeout = function(callback, after, arg1, arg2, arg3) { if (typeof callback !== 'function') { throw new TypeError('"callback" argument must be a function'); } - after *= 1; // coalesce to number or NaN - - if (!(after >= 1 && after <= TIMEOUT_MAX)) { - after = 1; // schedule on next tick, follows browser behaviour + var len = arguments.length; + var args; + if (len === 3) { + args = [arg1]; + } else if (len === 4) { + args = [arg1, arg2]; + } else if (len > 4) { + args = [arg1, arg2, arg3]; + for (var i = 5; i < len; i++) + // extend array dynamically, makes .apply run much faster in v6.0.0 + args[i - 2] = arguments[i]; } - var timer = new Timeout(after); - var length = arguments.length; - var ontimeout = callback; - switch (length) { - // fast cases - case 1: - case 2: - break; - case 3: - ontimeout = () => callback.call(timer, arguments[2]); - break; - case 4: - ontimeout = () => callback.call(timer, arguments[2], arguments[3]); - break; - case 5: - ontimeout = - () => callback.call(timer, arguments[2], arguments[3], arguments[4]); - break; - // slow case - default: - var args = new Array(length - 2); - for (var i = 2; i < length; i++) - args[i - 2] = arguments[i]; - ontimeout = () => callback.apply(timer, args); - break; - } - timer._onTimeout = ontimeout; + return createSingleTimeout(callback, after, args); +}; - if (process.domain) timer.domain = process.domain; +function createSingleTimeout(callback, after, args) { + after *= 1; // coalesce to number or NaN + if (!(after >= 1 && after <= TIMEOUT_MAX)) + after = 1; // schedule on next tick, follows browser behaviour + + var timer = new Timeout(after, callback, args); + if (process.domain) + timer.domain = process.domain; active(timer); return timer; -}; +} + + +function ontimeout(timer) { + var args = timer._timerArgs; + var callback = timer._onTimeout; + if (!args) + callback.call(timer); + else { + switch (args.length) { + case 1: + callback.call(timer, args[0]); + break; + case 2: + callback.call(timer, args[0], args[1]); + break; + case 3: + callback.call(timer, args[0], args[1], args[2]); + break; + default: + callback.apply(timer, args); + } + } + if (timer._repeat) + rearm(timer); +} + + +function rearm(timer) { + // If timer is unref'd (or was - it's permanently removed from the list.) + if (timer._handle && timer instanceof Timeout) { + timer._handle.start(timer._repeat); + } else { + timer._idleTimeout = timer._repeat; + active(timer); + } +} const clearTimeout = exports.clearTimeout = function(timer) { @@ -376,66 +406,41 @@ const clearTimeout = exports.clearTimeout = function(timer) { }; -exports.setInterval = function(callback, repeat) { +exports.setInterval = function(callback, repeat, arg1, arg2, arg3) { if (typeof callback !== 'function') { throw new TypeError('"callback" argument must be a function'); } - repeat *= 1; // coalesce to number or NaN + var len = arguments.length; + var args; + if (len === 3) { + args = [arg1]; + } else if (len === 4) { + args = [arg1, arg2]; + } else if (len > 4) { + args = [arg1, arg2, arg3]; + for (var i = 5; i < len; i++) + // extend array dynamically, makes .apply run much faster in v6.0.0 + args[i - 2] = arguments[i]; + } + + return createRepeatTimeout(callback, repeat, args); +}; - if (!(repeat >= 1 && repeat <= TIMEOUT_MAX)) { +function createRepeatTimeout(callback, repeat, args) { + repeat *= 1; // coalesce to number or NaN + if (!(repeat >= 1 && repeat <= TIMEOUT_MAX)) repeat = 1; // schedule on next tick, follows browser behaviour - } - var timer = new Timeout(repeat); - var length = arguments.length; - var ontimeout = callback; - switch (length) { - case 1: - case 2: - break; - case 3: - ontimeout = () => callback.call(timer, arguments[2]); - break; - case 4: - ontimeout = () => callback.call(timer, arguments[2], arguments[3]); - break; - case 5: - ontimeout = - () => callback.call(timer, arguments[2], arguments[3], arguments[4]); - break; - default: - var args = new Array(length - 2); - for (var i = 2; i < length; i += 1) - args[i - 2] = arguments[i]; - ontimeout = () => callback.apply(timer, args); - break; - } - timer._onTimeout = wrapper; - timer._repeat = ontimeout; + var timer = new Timeout(repeat, callback, args); + timer._repeat = repeat; + if (process.domain) + timer.domain = process.domain; - if (process.domain) timer.domain = process.domain; active(timer); return timer; - - function wrapper() { - timer._repeat(); - - // Timer might be closed - no point in restarting it - if (!timer._repeat) - return; - - // If timer is unref'd (or was - it's permanently removed from the list.) - if (this._handle) { - this._handle.start(repeat); - } else { - timer._idleTimeout = repeat; - active(timer); - } - } -}; - +} exports.clearInterval = function(timer) { if (timer && timer._repeat) { @@ -445,19 +450,20 @@ exports.clearInterval = function(timer) { }; -function Timeout(after) { +function Timeout(after, callback, args) { this._called = false; this._idleTimeout = after; this._idlePrev = this; this._idleNext = this; this._idleStart = null; - this._onTimeout = null; + this._onTimeout = callback; + this._timerArgs = args; this._repeat = null; } function unrefdHandle() { - this.owner._onTimeout(); + ontimeout(this.owner); if (!this.owner._repeat) this.owner.close(); } diff --git a/test/message/timeout_throw.out b/test/message/timeout_throw.out index 2a049a157a..9ef4f63e3d 100644 --- a/test/message/timeout_throw.out +++ b/test/message/timeout_throw.out @@ -3,5 +3,6 @@ ^ ReferenceError: undefined_reference_error_maker is not defined at Timeout._onTimeout (*test*message*timeout_throw.js:*:*) + at ontimeout (timers.js:*:*) at tryOnTimeout (timers.js:*:*) at Timer.listOnTimeout (timers.js:*:*)