From c0367dc71edd9e0af34dddff4f0e03a806a48054 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Caleb=20=E3=83=84=20Everett?= Date: Mon, 9 Sep 2019 16:56:32 -0700 Subject: [PATCH] lib: add Timer/Immediate to promisifyed setTimer/setImmediate The current implementation of the promisified setTimeout and setImmediate does not make the Timeout or Immediate available, so they cannot be canceled or unreffed. The docs and code follow the pattern established by child_process - lib/child_process.js#L150-L171 - doc/api/child_process.md#L217-L222 --- doc/api/timers.md | 23 +++++++++++++++---- lib/timers.js | 13 +++++++---- .../test-timer-immediate-async-cleared.js | 10 ++++++++ test/parallel/test-timer-immediate-async.js | 12 ++++++++++ .../test-timer-timeout-async-cleared.js | 10 ++++++++ test/parallel/test-timer-timeout-async.js | 12 ++++++++++ 6 files changed, 72 insertions(+), 8 deletions(-) create mode 100644 test/parallel/test-timer-immediate-async-cleared.js create mode 100644 test/parallel/test-timer-immediate-async.js create mode 100644 test/parallel/test-timer-timeout-async-cleared.js create mode 100644 test/parallel/test-timer-timeout-async.js diff --git a/doc/api/timers.md b/doc/api/timers.md index 66ee2ca97e8455..2f53d805227d49 100644 --- a/doc/api/timers.md +++ b/doc/api/timers.md @@ -151,8 +151,9 @@ next event loop iteration. If `callback` is not a function, a [`TypeError`][] will be thrown. -This method has a custom variant for promises that is available using -[`util.promisify()`][]: +If this method is invoked as its [`util.promisify()`][]ed version, it returns a +Promise. The created `Immediate` is attached to the Promise as a `immediate` +property. ```js const util = require('util'); @@ -170,6 +171,13 @@ async function timerExample() { console.log('After I/O callbacks'); } timerExample(); + +// You can access the immediate on promise.immediate +const promise = setImmediatePromise(); +promise.then(() => { + // This callback is never called because the Immediate is cleared +}); +clearImmediate(promise.immediate); ``` ### setInterval(callback, delay[, ...args]) @@ -213,8 +221,9 @@ will be set to `1`. Non-integer delays are truncated to an integer. If `callback` is not a function, a [`TypeError`][] will be thrown. -This method has a custom variant for promises that is available using -[`util.promisify()`][]: +If this method is invoked as its [`util.promisify()`][]ed version, it returns a +Promise. The created `Timeout` is attached to the Promise as a `timeout` +property. ```js const util = require('util'); @@ -224,6 +233,12 @@ setTimeoutPromise(40, 'foobar').then((value) => { // value === 'foobar' (passing values is optional) // This is executed after about 40 milliseconds. }); + +const promise = setTimeoutPromise(40, 'foobar'); +promise.then((value) => { + // This is never executed because the timeout is cleared +}); +clearTimeout(promise.timeout); ``` ## Cancelling Timers diff --git a/lib/timers.js b/lib/timers.js index 98acbf8fa44152..09c52cc4ce7485 100644 --- a/lib/timers.js +++ b/lib/timers.js @@ -147,9 +147,11 @@ function setTimeout(callback, after, arg1, arg2, arg3) { setTimeout[customPromisify] = function(after, value) { const args = value !== undefined ? [value] : value; - return new Promise((resolve) => { - active(new Timeout(resolve, after, args, false)); - }); + let resolve; + const promise = new Promise((res) => resolve = res); + promise.timeout = new Timeout(resolve, after, args, false); + active(promise.timeout); + return promise; }; function clearTimeout(timer) { @@ -272,7 +274,10 @@ function setImmediate(callback, arg1, arg2, arg3) { } setImmediate[customPromisify] = function(value) { - return new Promise((resolve) => new Immediate(resolve, [value])); + let resolve; + const promise = new Promise((res) => resolve = res); + promise.immediate = new Immediate(resolve, [value]); + return promise; }; function clearImmediate(immediate) { diff --git a/test/parallel/test-timer-immediate-async-cleared.js b/test/parallel/test-timer-immediate-async-cleared.js new file mode 100644 index 00000000000000..5fa5323fb28c74 --- /dev/null +++ b/test/parallel/test-timer-immediate-async-cleared.js @@ -0,0 +1,10 @@ +'use strict'; +const { mustNotCall } = require('../common'); +const { promisify } = require('util'); + +const setImmediateAsync = promisify(setImmediate); + +const expected = ['foo', 'bar', 'baz']; +const promise = setImmediateAsync(...expected); +promise.then(() => mustNotCall('expected immediate to be cleared')); +clearImmediate(promise.immediate); diff --git a/test/parallel/test-timer-immediate-async.js b/test/parallel/test-timer-immediate-async.js new file mode 100644 index 00000000000000..b9891202e686cb --- /dev/null +++ b/test/parallel/test-timer-immediate-async.js @@ -0,0 +1,12 @@ +'use strict'; +require('../common'); +const { strictEqual } = require('assert'); +const { promisify } = require('util'); + +const setImmediateAsync = promisify(setImmediate); + +const expected = ['foo', 'bar', 'baz']; +// N.B. the promisified version of setImmediate will resolve with the _first_ +// value, not an array of all values. +setImmediateAsync(...expected) + .then((actual) => strictEqual(actual, expected[0])); diff --git a/test/parallel/test-timer-timeout-async-cleared.js b/test/parallel/test-timer-timeout-async-cleared.js new file mode 100644 index 00000000000000..043181cb3ffecc --- /dev/null +++ b/test/parallel/test-timer-timeout-async-cleared.js @@ -0,0 +1,10 @@ +'use strict'; +const { mustNotCall } = require('../common'); +const { promisify } = require('util'); + +const setTimeoutAsync = promisify(setTimeout); + +const expected = ['foo', 'bar', 'baz']; +const promise = setTimeoutAsync(10, ...expected); +promise.then(() => mustNotCall('expected timeout to be cleared')); +clearTimeout(promise.timeout); diff --git a/test/parallel/test-timer-timeout-async.js b/test/parallel/test-timer-timeout-async.js new file mode 100644 index 00000000000000..618713bc93e44b --- /dev/null +++ b/test/parallel/test-timer-timeout-async.js @@ -0,0 +1,12 @@ +'use strict'; +require('../common'); +const { strictEqual } = require('assert'); +const { promisify } = require('util'); + +const setTimeoutAsync = promisify(setTimeout); + +const expected = ['foo', 'bar', 'baz']; +// N.B. the promisified version of setTimeout will resolve with the _first_ +// value, not an array of all values. +setTimeoutAsync(10, ...expected) + .then((actual) => strictEqual(actual, expected[0]));