timers: add promisify support
Add support for `util.promisify(setTimeout)` and `util.promisify(setImmediate)` as a proof-of-concept implementation. `clearTimeout()` and `clearImmediate()` do not work on those Promises. Includes documentation and tests. PR-URL: https://github.com/nodejs/node/pull/12442 Reviewed-By: Benjamin Gruenbaum <benjamingr@gmail.com> Reviewed-By: Matteo Collina <matteo.collina@gmail.com> Reviewed-By: James M Snell <jasnell@gmail.com> Reviewed-By: Myles Borins <myles.borins@gmail.com> Reviewed-By: Evan Lucas <evanlucas@me.com> Reviewed-By: William Kapke <william.kapke@gmail.com> Reviewed-By: Timothy Gu <timothygu99@gmail.com> Reviewed-By: Teddy Katz <teddy.katz@gmail.com>
This commit is contained in:
parent
e965ed16c1
commit
e7c51454b0
@ -85,6 +85,27 @@ next event loop iteration.
|
|||||||
|
|
||||||
If `callback` is not a function, a [`TypeError`][] will be thrown.
|
If `callback` is not a function, a [`TypeError`][] will be thrown.
|
||||||
|
|
||||||
|
*Note*: This method has a custom variant for promises that is available using
|
||||||
|
[`util.promisify()`][]:
|
||||||
|
|
||||||
|
```js
|
||||||
|
const util = require('util');
|
||||||
|
const setImmediatePromise = util.promisify(setImmediate);
|
||||||
|
|
||||||
|
setImmediatePromise('foobar').then((value) => {
|
||||||
|
// value === 'foobar' (passing values is optional)
|
||||||
|
// This is executed after all I/O callbacks.
|
||||||
|
});
|
||||||
|
|
||||||
|
// or with async function
|
||||||
|
async function timerExample() {
|
||||||
|
console.log('Before I/O callbacks');
|
||||||
|
await setImmediatePromise();
|
||||||
|
console.log('After I/O callbacks');
|
||||||
|
}
|
||||||
|
timerExample();
|
||||||
|
```
|
||||||
|
|
||||||
### setInterval(callback, delay[, ...args])
|
### setInterval(callback, delay[, ...args])
|
||||||
<!-- YAML
|
<!-- YAML
|
||||||
added: v0.0.1
|
added: v0.0.1
|
||||||
@ -126,12 +147,28 @@ will be set to `1`.
|
|||||||
|
|
||||||
If `callback` is not a function, a [`TypeError`][] will be thrown.
|
If `callback` is not a function, a [`TypeError`][] will be thrown.
|
||||||
|
|
||||||
|
*Note*: This method has a custom variant for promises that is available using
|
||||||
|
[`util.promisify()`][]:
|
||||||
|
|
||||||
|
```js
|
||||||
|
const util = require('util');
|
||||||
|
const setTimeoutPromise = util.promisify(setTimeout);
|
||||||
|
|
||||||
|
setTimeoutPromise(40, 'foobar').then((value) => {
|
||||||
|
// value === 'foobar' (passing values is optional)
|
||||||
|
// This is executed after about 40 milliseconds.
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
## Cancelling Timers
|
## Cancelling Timers
|
||||||
|
|
||||||
The [`setImmediate()`][], [`setInterval()`][], and [`setTimeout()`][] methods
|
The [`setImmediate()`][], [`setInterval()`][], and [`setTimeout()`][] methods
|
||||||
each return objects that represent the scheduled timers. These can be used to
|
each return objects that represent the scheduled timers. These can be used to
|
||||||
cancel the timer and prevent it from triggering.
|
cancel the timer and prevent it from triggering.
|
||||||
|
|
||||||
|
It is not possible to cancel timers that were created using the promisified
|
||||||
|
variants of [`setImmediate()`][], [`setTimeout()`][].
|
||||||
|
|
||||||
### clearImmediate(immediate)
|
### clearImmediate(immediate)
|
||||||
<!-- YAML
|
<!-- YAML
|
||||||
added: v0.9.1
|
added: v0.9.1
|
||||||
@ -168,4 +205,5 @@ Cancels a `Timeout` object created by [`setTimeout()`][].
|
|||||||
[`setImmediate()`]: timers.html#timers_setimmediate_callback_args
|
[`setImmediate()`]: timers.html#timers_setimmediate_callback_args
|
||||||
[`setInterval()`]: timers.html#timers_setinterval_callback_delay_args
|
[`setInterval()`]: timers.html#timers_setinterval_callback_delay_args
|
||||||
[`setTimeout()`]: timers.html#timers_settimeout_callback_delay_args
|
[`setTimeout()`]: timers.html#timers_settimeout_callback_delay_args
|
||||||
|
[`util.promisify()`]: util.html#util_util_promisify_original
|
||||||
[the Node.js Event Loop]: https://nodejs.org/en/docs/guides/event-loop-timers-and-nexttick
|
[the Node.js Event Loop]: https://nodejs.org/en/docs/guides/event-loop-timers-and-nexttick
|
||||||
|
@ -23,6 +23,8 @@
|
|||||||
|
|
||||||
const TimerWrap = process.binding('timer_wrap').Timer;
|
const TimerWrap = process.binding('timer_wrap').Timer;
|
||||||
const L = require('internal/linkedlist');
|
const L = require('internal/linkedlist');
|
||||||
|
const internalUtil = require('internal/util');
|
||||||
|
const { createPromise, promiseResolve } = process.binding('util');
|
||||||
const assert = require('assert');
|
const assert = require('assert');
|
||||||
const util = require('util');
|
const util = require('util');
|
||||||
const debug = util.debuglog('timer');
|
const debug = util.debuglog('timer');
|
||||||
@ -364,7 +366,7 @@ exports.enroll = function(item, msecs) {
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
|
|
||||||
exports.setTimeout = function(callback, after, arg1, arg2, arg3) {
|
function setTimeout(callback, after, arg1, arg2, arg3) {
|
||||||
if (typeof callback !== 'function') {
|
if (typeof callback !== 'function') {
|
||||||
throw new TypeError('"callback" argument must be a function');
|
throw new TypeError('"callback" argument must be a function');
|
||||||
}
|
}
|
||||||
@ -383,8 +385,16 @@ exports.setTimeout = function(callback, after, arg1, arg2, arg3) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return createSingleTimeout(callback, after, args);
|
return createSingleTimeout(callback, after, args);
|
||||||
|
}
|
||||||
|
|
||||||
|
setTimeout[internalUtil.promisify.custom] = function(after, value) {
|
||||||
|
const promise = createPromise();
|
||||||
|
createSingleTimeout(promise, after, [value]);
|
||||||
|
return promise;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
exports.setTimeout = setTimeout;
|
||||||
|
|
||||||
function createSingleTimeout(callback, after, args) {
|
function createSingleTimeout(callback, after, args) {
|
||||||
after *= 1; // coalesce to number or NaN
|
after *= 1; // coalesce to number or NaN
|
||||||
if (!(after >= 1 && after <= TIMEOUT_MAX))
|
if (!(after >= 1 && after <= TIMEOUT_MAX))
|
||||||
@ -403,6 +413,8 @@ function createSingleTimeout(callback, after, args) {
|
|||||||
function ontimeout(timer) {
|
function ontimeout(timer) {
|
||||||
var args = timer._timerArgs;
|
var args = timer._timerArgs;
|
||||||
var callback = timer._onTimeout;
|
var callback = timer._onTimeout;
|
||||||
|
if (typeof callback !== 'function')
|
||||||
|
return promiseResolve(callback, args[0]);
|
||||||
if (!args)
|
if (!args)
|
||||||
callback.call(timer);
|
callback.call(timer);
|
||||||
else {
|
else {
|
||||||
@ -687,6 +699,8 @@ function tryOnImmediate(immediate, oldTail) {
|
|||||||
function runCallback(timer) {
|
function runCallback(timer) {
|
||||||
const argv = timer._argv;
|
const argv = timer._argv;
|
||||||
const argc = argv ? argv.length : 0;
|
const argc = argv ? argv.length : 0;
|
||||||
|
if (typeof timer._callback !== 'function')
|
||||||
|
return promiseResolve(timer._callback, argv[0]);
|
||||||
switch (argc) {
|
switch (argc) {
|
||||||
// fast-path callbacks with 0-3 arguments
|
// fast-path callbacks with 0-3 arguments
|
||||||
case 0:
|
case 0:
|
||||||
@ -715,7 +729,7 @@ function Immediate() {
|
|||||||
this.domain = process.domain;
|
this.domain = process.domain;
|
||||||
}
|
}
|
||||||
|
|
||||||
exports.setImmediate = function(callback, arg1, arg2, arg3) {
|
function setImmediate(callback, arg1, arg2, arg3) {
|
||||||
if (typeof callback !== 'function') {
|
if (typeof callback !== 'function') {
|
||||||
throw new TypeError('"callback" argument must be a function');
|
throw new TypeError('"callback" argument must be a function');
|
||||||
}
|
}
|
||||||
@ -740,8 +754,16 @@ exports.setImmediate = function(callback, arg1, arg2, arg3) {
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
return createImmediate(args, callback);
|
return createImmediate(args, callback);
|
||||||
|
}
|
||||||
|
|
||||||
|
setImmediate[internalUtil.promisify.custom] = function(value) {
|
||||||
|
const promise = createPromise();
|
||||||
|
createImmediate([value], promise);
|
||||||
|
return promise;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
exports.setImmediate = setImmediate;
|
||||||
|
|
||||||
function createImmediate(args, callback) {
|
function createImmediate(args, callback) {
|
||||||
// declaring it `const immediate` causes v6.0.0 to deoptimize this function
|
// declaring it `const immediate` causes v6.0.0 to deoptimize this function
|
||||||
var immediate = new Immediate();
|
var immediate = new Immediate();
|
||||||
|
40
test/parallel/test-timers-promisified.js
Normal file
40
test/parallel/test-timers-promisified.js
Normal file
@ -0,0 +1,40 @@
|
|||||||
|
'use strict';
|
||||||
|
const common = require('../common');
|
||||||
|
const assert = require('assert');
|
||||||
|
const timers = require('timers');
|
||||||
|
const { promisify } = require('util');
|
||||||
|
|
||||||
|
/* eslint-disable no-restricted-syntax */
|
||||||
|
|
||||||
|
common.crashOnUnhandledRejection();
|
||||||
|
|
||||||
|
const setTimeout = promisify(timers.setTimeout);
|
||||||
|
const setImmediate = promisify(timers.setImmediate);
|
||||||
|
|
||||||
|
{
|
||||||
|
const promise = setTimeout(1);
|
||||||
|
promise.then(common.mustCall((value) => {
|
||||||
|
assert.strictEqual(value, undefined);
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
const promise = setTimeout(1, 'foobar');
|
||||||
|
promise.then(common.mustCall((value) => {
|
||||||
|
assert.strictEqual(value, 'foobar');
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
const promise = setImmediate();
|
||||||
|
promise.then(common.mustCall((value) => {
|
||||||
|
assert.strictEqual(value, undefined);
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
const promise = setImmediate('foobar');
|
||||||
|
promise.then(common.mustCall((value) => {
|
||||||
|
assert.strictEqual(value, 'foobar');
|
||||||
|
}));
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user