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 4ebe38b

Browse filesBrowse files
Linkgorondanielleadams
authored andcommitted
timers: introduce setInterval async iterator
Added setInterval async generator to timers\promises. Utilises async generators to provide an iterator compatible with `for await`. Co-Authored-By: Fabian Cook <hello@fabiancook.dev> fix message PR-URL: #37153 Reviewed-By: Benjamin Gruenbaum <benjamingr@gmail.com> Reviewed-By: James M Snell <jasnell@gmail.com>
1 parent bfe0b46 commit 4ebe38b
Copy full SHA for 4ebe38b

File tree

Expand file treeCollapse file tree

3 files changed

+327
-13
lines changed
Open diff view settings
Filter options
Expand file treeCollapse file tree

3 files changed

+327
-13
lines changed
Open diff view settings
Collapse file

‎doc/api/timers.md‎

Copy file name to clipboardExpand all lines: doc/api/timers.md
+32Lines changed: 32 additions & 0 deletions
  • Display the source diff
  • Display the rich diff
Original file line numberDiff line numberDiff line change
@@ -363,6 +363,38 @@ added: v15.0.0
363363
* `signal` {AbortSignal} An optional `AbortSignal` that can be used to
364364
cancel the scheduled `Immediate`.
365365

366+
### `timersPromises.setInterval([delay[, value[, options]]])`
367+
<!-- YAML
368+
added: REPLACEME
369+
-->
370+
371+
Returns an async iterator that generates values in an interval of `delay` ms.
372+
373+
* `delay` {number} The number of milliseconds to wait between iterations.
374+
**Default**: `1`.
375+
* `value` {any} A value with which the iterator returns.
376+
* `options` {Object}
377+
* `ref` {boolean} Set to `false` to indicate that the scheduled `Timeout`
378+
between iterations should not require the Node.js event loop to
379+
remain active.
380+
**Default**: `true`.
381+
* `signal` {AbortSignal} An optional `AbortSignal` that can be used to
382+
cancel the scheduled `Timeout` between operations.
383+
384+
```js
385+
(async function() {
386+
const { setInterval } = require('timers/promises');
387+
const interval = 100;
388+
for await (const startTime of setInterval(interval, Date.now())) {
389+
const now = Date.now();
390+
console.log(now);
391+
if ((now - startTime) > 1000)
392+
break;
393+
}
394+
console.log(Date.now());
395+
})();
396+
```
397+
366398
[Event Loop]: https://nodejs.org/en/docs/guides/event-loop-timers-and-nexttick/#setimmediate-vs-settimeout
367399
[`AbortController`]: globals.md#globals_class_abortcontroller
368400
[`TypeError`]: errors.md#errors_class_typeerror
Collapse file

‎lib/timers/promises.js‎

Copy file name to clipboardExpand all lines: lib/timers/promises.js
+57-1Lines changed: 57 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,11 @@ const {
1818
codes: { ERR_INVALID_ARG_TYPE }
1919
} = require('internal/errors');
2020

21-
const { validateAbortSignal } = require('internal/validators');
21+
const {
22+
validateAbortSignal,
23+
validateBoolean,
24+
validateObject,
25+
} = require('internal/validators');
2226

2327
function cancelListenerHandler(clear, reject) {
2428
if (!this._destroyed) {
@@ -111,7 +115,59 @@ function setImmediate(value, options = {}) {
111115
() => signal.removeEventListener('abort', oncancel)) : ret;
112116
}
113117

118+
async function* setInterval(after, value, options = {}) {
119+
validateObject(options, 'options');
120+
const { signal, ref = true } = options;
121+
validateAbortSignal(signal, 'options.signal');
122+
validateBoolean(ref, 'options.ref');
123+
124+
if (signal?.aborted)
125+
throw new AbortError();
126+
127+
let onCancel;
128+
let interval;
129+
try {
130+
let notYielded = 0;
131+
let callback;
132+
interval = new Timeout(() => {
133+
notYielded++;
134+
if (callback) {
135+
callback();
136+
callback = undefined;
137+
}
138+
}, after, undefined, true, true);
139+
if (!ref) interval.unref();
140+
insert(interval, interval._idleTimeout);
141+
if (signal) {
142+
onCancel = () => {
143+
// eslint-disable-next-line no-undef
144+
clearInterval(interval);
145+
if (callback) {
146+
callback(PromiseReject(new AbortError()));
147+
callback = undefined;
148+
}
149+
};
150+
signal.addEventListener('abort', onCancel, { once: true });
151+
}
152+
153+
while (!signal?.aborted) {
154+
if (notYielded === 0) {
155+
await new Promise((resolve) => callback = resolve);
156+
}
157+
for (; notYielded > 0; notYielded--) {
158+
yield value;
159+
}
160+
}
161+
throw new AbortError();
162+
} finally {
163+
// eslint-disable-next-line no-undef
164+
clearInterval(interval);
165+
signal?.removeEventListener('abort', onCancel);
166+
}
167+
}
168+
114169
module.exports = {
115170
setTimeout,
116171
setImmediate,
172+
setInterval,
117173
};

0 commit comments

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