UNPKG

nstdlib-nightly

Version:

Node.js standard library converted to runtime-agnostic ES modules.

233 lines (216 loc) 6.59 kB
// Source: https://github.com/nodejs/node/blob/65eff1eb/lib/timers/promises.js import { Timeout, Immediate, insert } from "nstdlib/lib/internal/timers"; import { clearImmediate, clearInterval, clearTimeout, } from "nstdlib/stub/timers"; import { AbortError, codes as __codes__ } from "nstdlib/lib/internal/errors"; import { validateAbortSignal, validateBoolean, validateObject, } from "nstdlib/lib/internal/validators"; import { kEmptyObject } from "nstdlib/lib/internal/util"; import * as __hoisted_internal_event_target__ from "nstdlib/lib/internal/event_target"; const { ERR_ILLEGAL_CONSTRUCTOR, ERR_INVALID_ARG_TYPE, ERR_INVALID_THIS } = __codes__; const kScheduler = Symbol("kScheduler"); let kResistStopPropagation; function cancelListenerHandler(clear, reject, signal) { if (!this._destroyed) { clear(this); reject(new AbortError(undefined, { cause: signal?.reason })); } } function setTimeout(after, value, options = kEmptyObject) { const args = value !== undefined ? [value] : value; if (options == null || typeof options !== "object") { return Promise.reject( new ERR_INVALID_ARG_TYPE("options", "Object", options), ); } const { signal, ref = true } = options; try { validateAbortSignal(signal, "options.signal"); } catch (err) { return Promise.reject(err); } if (typeof ref !== "boolean") { return Promise.reject( new ERR_INVALID_ARG_TYPE("options.ref", "boolean", ref), ); } if (signal?.aborted) { return Promise.reject(new AbortError(undefined, { cause: signal.reason })); } let oncancel; const ret = new Promise((resolve, reject) => { const timeout = new Timeout(resolve, after, args, false, ref); insert(timeout, timeout._idleTimeout); if (signal) { oncancel = Function.prototype.bind.call( cancelListenerHandler, timeout, clearTimeout, reject, signal, ); kResistStopPropagation ??= __hoisted_internal_event_target__.kResistStopPropagation; signal.addEventListener("abort", oncancel, { __proto__: null, [kResistStopPropagation]: true, }); } }); return oncancel !== undefined ? Promise.prototype.finally.call(ret, () => signal.removeEventListener("abort", oncancel), ) : ret; } function setImmediate(value, options = kEmptyObject) { if (options == null || typeof options !== "object") { return Promise.reject( new ERR_INVALID_ARG_TYPE("options", "Object", options), ); } const { signal, ref = true } = options; try { validateAbortSignal(signal, "options.signal"); } catch (err) { return Promise.reject(err); } if (typeof ref !== "boolean") { return Promise.reject( new ERR_INVALID_ARG_TYPE("options.ref", "boolean", ref), ); } if (signal?.aborted) { return Promise.reject(new AbortError(undefined, { cause: signal.reason })); } let oncancel; const ret = new Promise((resolve, reject) => { const immediate = new Immediate(resolve, [value]); if (!ref) immediate.unref(); if (signal) { oncancel = Function.prototype.bind.call( cancelListenerHandler, immediate, clearImmediate, reject, signal, ); kResistStopPropagation ??= __hoisted_internal_event_target__.kResistStopPropagation; signal.addEventListener("abort", oncancel, { __proto__: null, [kResistStopPropagation]: true, }); } }); return oncancel !== undefined ? Promise.prototype.finally.call(ret, () => signal.removeEventListener("abort", oncancel), ) : ret; } async function* setInterval(after, value, options = kEmptyObject) { validateObject(options, "options"); const { signal, ref = true } = options; validateAbortSignal(signal, "options.signal"); validateBoolean(ref, "options.ref"); if (signal?.aborted) throw new AbortError(undefined, { cause: signal?.reason }); let onCancel; let interval; try { let notYielded = 0; let callback; interval = new Timeout( () => { notYielded++; if (callback) { callback(); callback = undefined; } }, after, undefined, true, ref, ); insert(interval, interval._idleTimeout); if (signal) { onCancel = () => { clearInterval(interval); if (callback) { callback( Promise.reject(new AbortError(undefined, { cause: signal.reason })), ); callback = undefined; } }; kResistStopPropagation ??= __hoisted_internal_event_target__.kResistStopPropagation; signal.addEventListener("abort", onCancel, { __proto__: null, once: true, [kResistStopPropagation]: true, }); } while (!signal?.aborted) { if (notYielded === 0) { await new Promise((resolve) => (callback = resolve)); } for (; notYielded > 0; notYielded--) { yield value; } } throw new AbortError(undefined, { cause: signal?.reason }); } finally { clearInterval(interval); signal?.removeEventListener("abort", onCancel); } } // TODO(@jasnell): Scheduler is an API currently being discussed by WICG // for Web Platform standardization: https://github.com/WICG/scheduling-apis // The scheduler.yield() and scheduler.wait() methods correspond roughly to // the awaitable setTimeout and setImmediate implementations here. This api // should be considered to be experimental until the spec for these are // finalized. Note, also, that Scheduler is expected to be defined as a global, // but while the API is experimental we shouldn't expose it as such. class Scheduler { constructor() { throw new ERR_ILLEGAL_CONSTRUCTOR(); } /** * @returns {Promise<void>} */ yield() { if (!this[kScheduler]) throw new ERR_INVALID_THIS("Scheduler"); return setImmediate(); } /** * @typedef {import('../internal/abort_controller').AbortSignal} AbortSignal * @param {number} delay * @param {{ signal?: AbortSignal }} [options] * @returns {Promise<void>} */ wait(delay, options) { if (!this[kScheduler]) throw new ERR_INVALID_THIS("Scheduler"); return setTimeout(delay, undefined, { signal: options?.signal }); } } export { setTimeout }; export { setImmediate }; export { setInterval }; const _export_scheduler_ = Reflect.construct( function () { this[kScheduler] = true; }, [], Scheduler, ); export { _export_scheduler_ as scheduler };