Skip to content

Commit 4ebe38b

Browse 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

File tree

3 files changed

+327
-13
lines changed

3 files changed

+327
-13
lines changed

doc/api/timers.md

+32
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

lib/timers/promises.js

+57-1
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)