Skip to content

Commit c0367dc

Browse files
committed
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
1 parent f2e35ff commit c0367dc

6 files changed

+72
-8
lines changed

doc/api/timers.md

+19-4
Original file line numberDiff line numberDiff line change
@@ -151,8 +151,9 @@ next event loop iteration.
151151

152152
If `callback` is not a function, a [`TypeError`][] will be thrown.
153153

154-
This method has a custom variant for promises that is available using
155-
[`util.promisify()`][]:
154+
If this method is invoked as its [`util.promisify()`][]ed version, it returns a
155+
Promise. The created `Immediate` is attached to the Promise as a `immediate`
156+
property.
156157

157158
```js
158159
const util = require('util');
@@ -170,6 +171,13 @@ async function timerExample() {
170171
console.log('After I/O callbacks');
171172
}
172173
timerExample();
174+
175+
// You can access the immediate on promise.immediate
176+
const promise = setImmediatePromise();
177+
promise.then(() => {
178+
// This callback is never called because the Immediate is cleared
179+
});
180+
clearImmediate(promise.immediate);
173181
```
174182

175183
### setInterval(callback, delay[, ...args])
@@ -213,8 +221,9 @@ will be set to `1`. Non-integer delays are truncated to an integer.
213221

214222
If `callback` is not a function, a [`TypeError`][] will be thrown.
215223

216-
This method has a custom variant for promises that is available using
217-
[`util.promisify()`][]:
224+
If this method is invoked as its [`util.promisify()`][]ed version, it returns a
225+
Promise. The created `Timeout` is attached to the Promise as a `timeout`
226+
property.
218227

219228
```js
220229
const util = require('util');
@@ -224,6 +233,12 @@ setTimeoutPromise(40, 'foobar').then((value) => {
224233
// value === 'foobar' (passing values is optional)
225234
// This is executed after about 40 milliseconds.
226235
});
236+
237+
const promise = setTimeoutPromise(40, 'foobar');
238+
promise.then((value) => {
239+
// This is never executed because the timeout is cleared
240+
});
241+
clearTimeout(promise.timeout);
227242
```
228243

229244
## Cancelling Timers

lib/timers.js

+9-4
Original file line numberDiff line numberDiff line change
@@ -147,9 +147,11 @@ function setTimeout(callback, after, arg1, arg2, arg3) {
147147

148148
setTimeout[customPromisify] = function(after, value) {
149149
const args = value !== undefined ? [value] : value;
150-
return new Promise((resolve) => {
151-
active(new Timeout(resolve, after, args, false));
152-
});
150+
let resolve;
151+
const promise = new Promise((res) => resolve = res);
152+
promise.timeout = new Timeout(resolve, after, args, false);
153+
active(promise.timeout);
154+
return promise;
153155
};
154156

155157
function clearTimeout(timer) {
@@ -272,7 +274,10 @@ function setImmediate(callback, arg1, arg2, arg3) {
272274
}
273275

274276
setImmediate[customPromisify] = function(value) {
275-
return new Promise((resolve) => new Immediate(resolve, [value]));
277+
let resolve;
278+
const promise = new Promise((res) => resolve = res);
279+
promise.immediate = new Immediate(resolve, [value]);
280+
return promise;
276281
};
277282

278283
function clearImmediate(immediate) {
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
'use strict';
2+
const { mustNotCall } = require('../common');
3+
const { promisify } = require('util');
4+
5+
const setImmediateAsync = promisify(setImmediate);
6+
7+
const expected = ['foo', 'bar', 'baz'];
8+
const promise = setImmediateAsync(...expected);
9+
promise.then(() => mustNotCall('expected immediate to be cleared'));
10+
clearImmediate(promise.immediate);
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
'use strict';
2+
require('../common');
3+
const { strictEqual } = require('assert');
4+
const { promisify } = require('util');
5+
6+
const setImmediateAsync = promisify(setImmediate);
7+
8+
const expected = ['foo', 'bar', 'baz'];
9+
// N.B. the promisified version of setImmediate will resolve with the _first_
10+
// value, not an array of all values.
11+
setImmediateAsync(...expected)
12+
.then((actual) => strictEqual(actual, expected[0]));
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
'use strict';
2+
const { mustNotCall } = require('../common');
3+
const { promisify } = require('util');
4+
5+
const setTimeoutAsync = promisify(setTimeout);
6+
7+
const expected = ['foo', 'bar', 'baz'];
8+
const promise = setTimeoutAsync(10, ...expected);
9+
promise.then(() => mustNotCall('expected timeout to be cleared'));
10+
clearTimeout(promise.timeout);
+12
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
'use strict';
2+
require('../common');
3+
const { strictEqual } = require('assert');
4+
const { promisify } = require('util');
5+
6+
const setTimeoutAsync = promisify(setTimeout);
7+
8+
const expected = ['foo', 'bar', 'baz'];
9+
// N.B. the promisified version of setTimeout will resolve with the _first_
10+
// value, not an array of all values.
11+
setTimeoutAsync(10, ...expected)
12+
.then((actual) => strictEqual(actual, expected[0]));

0 commit comments

Comments
 (0)