diff options
author | James M Snell <jasnell@gmail.com> | 2020-06-18 23:22:17 +0300 |
---|---|---|
committer | James M Snell <jasnell@gmail.com> | 2020-06-23 00:29:49 +0300 |
commit | a8904e8eeea3ca513de588424bb99f6a7cdfc598 (patch) | |
tree | 35d9fc741714a1c71939b91c9b8ef69cdbf65f4c /lib/timers | |
parent | bfc0e3f0b0bc99deffe99586bd9f03a27c6ed098 (diff) |
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: https://github.com/nodejs/node/pull/33950
Reviewed-By: Denys Otrishko <shishugi@gmail.com>
Reviewed-By: Benjamin Gruenbaum <benjamingr@gmail.com>
Diffstat (limited to 'lib/timers')
-rw-r--r-- | lib/timers/promises.js | 124 |
1 files changed, 124 insertions, 0 deletions
diff --git a/lib/timers/promises.js b/lib/timers/promises.js new file mode 100644 index 00000000000..78197fe86f6 --- /dev/null +++ b/lib/timers/promises.js @@ -0,0 +1,124 @@ +'use strict'; + +const { + Promise, + PromiseReject, +} = primordials; + +const { + Timeout, + Immediate, + insert +} = require('internal/timers'); + +const { + hideStackFrames, + codes: { ERR_INVALID_ARG_TYPE } +} = require('internal/errors'); + +let DOMException; + +const lazyDOMException = hideStackFrames((message) => { + if (DOMException === undefined) + DOMException = internalBinding('messaging').DOMException; + return new DOMException(message); +}); + +function setTimeout(after, value, options = {}) { + const args = value !== undefined ? [value] : value; + if (options == null || typeof options !== 'object') { + return PromiseReject( + new ERR_INVALID_ARG_TYPE( + 'options', + 'Object', + options)); + } + const { signal, ref = true } = options; + if (signal !== undefined && + (signal === null || + typeof signal !== 'object' || + !('aborted' in signal))) { + return PromiseReject( + new ERR_INVALID_ARG_TYPE( + 'options.signal', + 'AbortSignal', + signal)); + } + if (typeof ref !== 'boolean') { + return PromiseReject( + new ERR_INVALID_ARG_TYPE( + 'options.ref', + 'boolean', + ref)); + } + // TODO(@jasnell): If a decision is made that this cannot be backported + // to 12.x, then this can be converted to use optional chaining to + // simplify the check. + if (signal && signal.aborted) + return PromiseReject(lazyDOMException('AbortError')); + return new Promise((resolve, reject) => { + const timeout = new Timeout(resolve, after, args, false, true); + if (!ref) timeout.unref(); + insert(timeout, timeout._idleTimeout); + if (signal) { + signal.addEventListener('abort', () => { + if (!timeout._destroyed) { + // eslint-disable-next-line no-undef + clearTimeout(timeout); + reject(lazyDOMException('AbortError')); + } + }, { once: true }); + } + }); +} + +function setImmediate(value, options = {}) { + if (options == null || typeof options !== 'object') { + return PromiseReject( + new ERR_INVALID_ARG_TYPE( + 'options', + 'Object', + options)); + } + const { signal, ref = true } = options; + if (signal !== undefined && + (signal === null || + typeof signal !== 'object' || + !('aborted' in signal))) { + return PromiseReject( + new ERR_INVALID_ARG_TYPE( + 'options.signal', + 'AbortSignal', + signal)); + } + if (typeof ref !== 'boolean') { + return PromiseReject( + new ERR_INVALID_ARG_TYPE( + 'options.ref', + 'boolean', + ref)); + } + // TODO(@jasnell): If a decision is made that this cannot be backported + // to 12.x, then this can be converted to use optional chaining to + // simplify the check. + if (signal && signal.aborted) + return PromiseReject(lazyDOMException('AbortError')); + return new Promise((resolve, reject) => { + const immediate = new Immediate(resolve, [value]); + if (!ref) immediate.unref(); + if (signal) { + signal.addEventListener('abort', () => { + if (!immediate._destroyed) { + // eslint-disable-next-line no-undef + clearImmediate(immediate); + reject(lazyDOMException('AbortError')); + } + }, { once: true }); + } + }); +} + +module.exports = { + setTimeout, + setImmediate, +}; |