Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings

Commit a8904e8

Browse filesBrowse files
committed
timers: introduce timers/promises
Move the promisified timers implementations into a new sub-module to avoid the need to promisify. The promisified versions now return the timers/promises versions. Also adds `ref` option to the promisified versions ```js const { setTimeout, setImmediate } = require('timers/promises'); setTimeout(10, null, { ref: false }) .then(console.log); setImmediate(null, { ref: false }) .then(console.log); ``` Signed-off-by: James M Snell <jasnell@gmail.com> PR-URL: #33950 Reviewed-By: Denys Otrishko <shishugi@gmail.com> Reviewed-By: Benjamin Gruenbaum <benjamingr@gmail.com>
1 parent bfc0e3f commit a8904e8
Copy full SHA for a8904e8

File tree

Expand file treeCollapse file tree

6 files changed

+250
-127
lines changed
Open diff view settings
Filter options
Expand file treeCollapse file tree

6 files changed

+250
-127
lines changed
Open diff view settings
Collapse file

‎doc/api/timers.md‎

Copy file name to clipboardExpand all lines: doc/api/timers.md
+34Lines changed: 34 additions & 0 deletions
  • Display the source diff
  • Display the rich diff
Original file line numberDiff line numberDiff line change
@@ -302,6 +302,40 @@ added: v0.0.1
302302

303303
Cancels a `Timeout` object created by [`setTimeout()`][].
304304

305+
## Timers Promises API
306+
307+
> Stability: 1 - Experimental
308+
309+
The `timers/promises` API provides an alternative set of timer functions
310+
that return `Promise` objects. The API is accessible via
311+
`require('timers/promises')`.
312+
313+
```js
314+
const timersPromises = require('timers/promises');
315+
```
316+
317+
### `timersPromises.setTimeout(delay\[, value\[, options\]\])
318+
319+
* `delay` {number} The number of milliseconds to wait before resolving the
320+
`Promise`.
321+
* `value` {any} A value with which the `Promise` is resolved.
322+
* `options` {Object}
323+
* `ref` {boolean} Set to `false` to indicate that the scheduled `Timeout`
324+
should not require the Node.js event loop to remain active.
325+
**Default**: `true`.
326+
* `signal` {AbortSignal} An optional `AbortSignal` that can be used to
327+
cancel the scheduled `Timeout`.
328+
329+
### `timersPromises.setImmediate(\[value\[, options\]\])
330+
331+
* `value` {any} A value with which the `Promise` is resolved.
332+
* `options` {Object}
333+
* `ref` {boolean} Set to `false` to indicate that the scheduled `Immediate`
334+
should not require the Node.js event loop to remain active.
335+
**Default**: `true`.
336+
* `signal` {AbortSignal} An optional `AbortSignal` that can be used to
337+
cancel the scheduled `Immediate`.
338+
305339
[Event Loop]: https://nodejs.org/en/docs/guides/event-loop-timers-and-nexttick/#setimmediate-vs-settimeout
306340
[`AbortController`]: globals.html#globals_class_abortcontroller
307341
[`TypeError`]: errors.html#errors_class_typeerror
Collapse file

‎lib/internal/timers.js‎

Copy file name to clipboardExpand all lines: lib/internal/timers.js
+43-1Lines changed: 43 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -84,7 +84,8 @@ const {
8484
scheduleTimer,
8585
toggleTimerRef,
8686
getLibuvNow,
87-
immediateInfo
87+
immediateInfo,
88+
toggleImmediateRef
8889
} = internalBinding('timers');
8990

9091
const {
@@ -590,12 +591,53 @@ function getTimerCallbacks(runNextTicks) {
590591
};
591592
}
592593

594+
class Immediate {
595+
constructor(callback, args) {
596+
this._idleNext = null;
597+
this._idlePrev = null;
598+
this._onImmediate = callback;
599+
this._argv = args;
600+
this._destroyed = false;
601+
this[kRefed] = false;
602+
603+
initAsyncResource(this, 'Immediate');
604+
605+
this.ref();
606+
immediateInfo[kCount]++;
607+
608+
immediateQueue.append(this);
609+
}
610+
611+
ref() {
612+
if (this[kRefed] === false) {
613+
this[kRefed] = true;
614+
if (immediateInfo[kRefCount]++ === 0)
615+
toggleImmediateRef(true);
616+
}
617+
return this;
618+
}
619+
620+
unref() {
621+
if (this[kRefed] === true) {
622+
this[kRefed] = false;
623+
if (--immediateInfo[kRefCount] === 0)
624+
toggleImmediateRef(false);
625+
}
626+
return this;
627+
}
628+
629+
hasRef() {
630+
return !!this[kRefed];
631+
}
632+
}
633+
593634
module.exports = {
594635
TIMEOUT_MAX,
595636
kTimeout: Symbol('timeout'), // For hiding Timeouts on other internals.
596637
async_id_symbol,
597638
trigger_async_id_symbol,
598639
Timeout,
640+
Immediate,
599641
kRefed,
600642
initAsyncResource,
601643
setUnrefTimeout,
Collapse file

‎lib/timers.js‎

Copy file name to clipboardExpand all lines: lib/timers.js
+19-126Lines changed: 19 additions & 126 deletions
Original file line numberDiff line numberDiff line change
@@ -23,15 +23,9 @@
2323

2424
const {
2525
MathTrunc,
26-
Promise,
26+
Object,
2727
} = primordials;
2828

29-
const {
30-
codes: { ERR_INVALID_ARG_TYPE }
31-
} = require('internal/errors');
32-
33-
let DOMException;
34-
3529
const {
3630
immediateInfo,
3731
toggleImmediateRef
@@ -40,13 +34,13 @@ const L = require('internal/linkedlist');
4034
const {
4135
async_id_symbol,
4236
Timeout,
37+
Immediate,
4338
decRefCount,
4439
immediateInfoFields: {
4540
kCount,
4641
kRefCount
4742
},
4843
kRefed,
49-
initAsyncResource,
5044
getTimerDuration,
5145
timerListMap,
5246
timerListQueue,
@@ -64,6 +58,8 @@ let debug = require('internal/util/debuglog').debuglog('timer', (fn) => {
6458
});
6559
const { validateCallback } = require('internal/validators');
6660

61+
let timersPromises;
62+
6763
const {
6864
destroyHooksExist,
6965
// The needed emit*() functions.
@@ -124,12 +120,6 @@ function enroll(item, msecs) {
124120
* DOM-style timers
125121
*/
126122

127-
function lazyDOMException(message) {
128-
if (DOMException === undefined)
129-
DOMException = internalBinding('messaging').DOMException;
130-
return new DOMException(message);
131-
}
132-
133123
function setTimeout(callback, after, arg1, arg2, arg3) {
134124
validateCallback(callback);
135125

@@ -160,44 +150,14 @@ function setTimeout(callback, after, arg1, arg2, arg3) {
160150
return timeout;
161151
}
162152

163-
setTimeout[customPromisify] = function(after, value, options = {}) {
164-
const args = value !== undefined ? [value] : value;
165-
if (options == null || typeof options !== 'object') {
166-
return Promise.reject(
167-
new ERR_INVALID_ARG_TYPE(
168-
'options',
169-
'Object',
170-
options));
153+
Object.defineProperty(setTimeout, customPromisify, {
154+
enumerable: true,
155+
get() {
156+
if (!timersPromises)
157+
timersPromises = require('timers/promises');
158+
return timersPromises.setTimeout;
171159
}
172-
const { signal } = options;
173-
if (signal !== undefined &&
174-
(signal === null ||
175-
typeof signal !== 'object' ||
176-
!('aborted' in signal))) {
177-
return Promise.reject(
178-
new ERR_INVALID_ARG_TYPE(
179-
'options.signal',
180-
'AbortSignal',
181-
signal));
182-
}
183-
// TODO(@jasnell): If a decision is made that this cannot be backported
184-
// to 12.x, then this can be converted to use optional chaining to
185-
// simplify the check.
186-
if (signal && signal.aborted)
187-
return Promise.reject(lazyDOMException('AbortError'));
188-
return new Promise((resolve, reject) => {
189-
const timeout = new Timeout(resolve, after, args, false, true);
190-
insert(timeout, timeout._idleTimeout);
191-
if (signal) {
192-
signal.addEventListener('abort', () => {
193-
if (!timeout._destroyed) {
194-
clearTimeout(timeout);
195-
reject(lazyDOMException('AbortError'));
196-
}
197-
}, { once: true });
198-
}
199-
});
200-
};
160+
});
201161

202162
function clearTimeout(timer) {
203163
if (timer && timer._onTimeout) {
@@ -248,46 +208,6 @@ Timeout.prototype.close = function() {
248208
return this;
249209
};
250210

251-
const Immediate = class Immediate {
252-
constructor(callback, args) {
253-
this._idleNext = null;
254-
this._idlePrev = null;
255-
this._onImmediate = callback;
256-
this._argv = args;
257-
this._destroyed = false;
258-
this[kRefed] = false;
259-
260-
initAsyncResource(this, 'Immediate');
261-
262-
this.ref();
263-
immediateInfo[kCount]++;
264-
265-
immediateQueue.append(this);
266-
}
267-
268-
ref() {
269-
if (this[kRefed] === false) {
270-
this[kRefed] = true;
271-
if (immediateInfo[kRefCount]++ === 0)
272-
toggleImmediateRef(true);
273-
}
274-
return this;
275-
}
276-
277-
unref() {
278-
if (this[kRefed] === true) {
279-
this[kRefed] = false;
280-
if (--immediateInfo[kRefCount] === 0)
281-
toggleImmediateRef(false);
282-
}
283-
return this;
284-
}
285-
286-
hasRef() {
287-
return !!this[kRefed];
288-
}
289-
};
290-
291211
function setImmediate(callback, arg1, arg2, arg3) {
292212
validateCallback(callback);
293213

@@ -314,42 +234,15 @@ function setImmediate(callback, arg1, arg2, arg3) {
314234
return new Immediate(callback, args);
315235
}
316236

317-
setImmediate[customPromisify] = function(value, options = {}) {
318-
if (options == null || typeof options !== 'object') {
319-
return Promise.reject(
320-
new ERR_INVALID_ARG_TYPE(
321-
'options',
322-
'Object',
323-
options));
237+
Object.defineProperty(setImmediate, customPromisify, {
238+
enumerable: true,
239+
get() {
240+
if (!timersPromises)
241+
timersPromises = require('timers/promises');
242+
return timersPromises.setImmediate;
324243
}
325-
const { signal } = options;
326-
if (signal !== undefined &&
327-
(signal === null ||
328-
typeof signal !== 'object' ||
329-
!('aborted' in signal))) {
330-
return Promise.reject(
331-
new ERR_INVALID_ARG_TYPE(
332-
'options.signal',
333-
'AbortSignal',
334-
signal));
335-
}
336-
// TODO(@jasnell): If a decision is made that this cannot be backported
337-
// to 12.x, then this can be converted to use optional chaining to
338-
// simplify the check.
339-
if (signal && signal.aborted)
340-
return Promise.reject(lazyDOMException('AbortError'));
341-
return new Promise((resolve, reject) => {
342-
const immediate = new Immediate(resolve, [value]);
343-
if (signal) {
344-
signal.addEventListener('abort', () => {
345-
if (!immediate._destroyed) {
346-
clearImmediate(immediate);
347-
reject(lazyDOMException('AbortError'));
348-
}
349-
}, { once: true });
350-
}
351-
});
352-
};
244+
});
245+
353246

354247
function clearImmediate(immediate) {
355248
if (!immediate || immediate._destroyed)

0 commit comments

Comments
0 (0)
Morty Proxy This is a proxified and sanitized view of the page, visit original site.