/
githubmirror
/
deno
Обзор
Документация
Войти
/
githubmirror
/
deno
Код
Запросы
0
Пакеты
0
Релизы
0
Аналитика
Безопасность
main
ext/node/polyfills/timers.ts
406 строк
11 KB
Bartek Iwańczuk
chore: bump node_compat test suite to Node.js 26.5.1 (#36391)
03 авг 2026, 12:52
Не верифицирован
03 авг 2026, 12:52
bca6182
Код
Авторство
О чём код?
// Copyright 2018-2026 the Deno authors. MIT license. (function () { const { core, primordials } = __bootstrap; const { FunctionPrototypeBind, ObjectCreate, ObjectDefineProperty, Promise, PromiseReject, PromiseWithResolvers, SafeArrayIterator, SafePromisePrototypeFinally, SymbolFor, } = primordials; // `node:test`'s `mock.timers` installs itself here while enabled. Routing the // interception through a module-level hook (rather than swapping the module's // exports) makes every call site honor the virtual clock regardless of how it // reached these functions: `globalThis`, `require("node:timers")`, or a static // `import { setTimeout } from "node:timers/promises"` binding captured before // `enable()`. Node's MockTimers likewise intercepts the timers modules, not // just the globals. `null` means no mocking is active. let mockTimers = null; const kInstallMockTimers = SymbolFor("Deno.internal.node.mockTimers"); const { getActiveTimer, Immediate, kDestroy, Timeout, } = core.loadExtScript("ext:deno_node/internal/timers.mjs"); const { validateAbortSignal, validateBoolean, validateFunction, validateNumber, validateObject, } = core.loadExtScript("ext:deno_node/internal/validators.mjs"); const { kEmptyObject, promisify } = core.loadExtScript( "ext:deno_node/internal/util.mjs", ); const { AbortError, ERR_ILLEGAL_CONSTRUCTOR, ERR_INVALID_THIS, } = core.loadExtScript("ext:deno_node/internal/errors.ts"); const lazyEventTarget = core.createLazyLoader( "ext:deno_node/internal/event_target.mjs", ); interface TimerOptions { signal?: AbortSignal | undefined; ref?: boolean | undefined; } function setTimeout( callback: (...args: unknown[]) => void, timeout?: number, ...args: unknown[] ) { validateFunction(callback, "callback"); if (mockTimers !== null && mockTimers._apiEnabled("setTimeout")) { return mockTimers._setTimeout(callback, timeout, args, false); } return new Timeout(callback, timeout, args, false, true); } function cancelListenerHandler( clear: typeof clearTimeout, reject: typeof PromiseReject, signal: AbortSignal | undefined, ) { if (!this._destroyed) { clear(this); reject(new AbortError(undefined, { cause: signal?.reason })); } } function setTimeoutPromise<T = void>( after: number | undefined, value: T, options: TimerOptions = kEmptyObject, ): Promise<T> { try { if (typeof after !== "undefined") { validateNumber(after, "delay"); } validateObject(options, "options"); if (typeof options?.signal !== "undefined") { validateAbortSignal(options.signal, "options.signal"); } if (typeof options?.ref !== "undefined") { validateBoolean(options.ref, "options.ref"); } } catch (err) { return PromiseReject(err); } const { signal, ref = true } = options; if (signal?.aborted) { return PromiseReject(new AbortError(undefined, { cause: signal.reason })); } let oncancel: EventListenerOrEventListenerObject | undefined; const { promise, resolve, reject } = PromiseWithResolvers(); // When mocking is active the timer is driven by the virtual clock, so the // promise resolves on `tick()`/`runAll()` instead of a real timer. const timeout = mockTimers !== null && mockTimers._apiEnabled("setTimeout") ? mockTimers._setTimeout(resolve, after, [value], false) : new Timeout(resolve, after, [value], false, ref); if (signal) { oncancel = FunctionPrototypeBind( cancelListenerHandler, timeout, clearTimeout, reject, signal, ); signal.addEventListener("abort", oncancel, { __proto__: null, [lazyEventTarget().kResistStopPropagation]: true, }); } return oncancel !== undefined ? SafePromisePrototypeFinally( promise, () => signal!.removeEventListener("abort", oncancel), ) : promise; } ObjectDefineProperty(setTimeoutPromise, "name", { __proto__: null, value: "setTimeout", }); ObjectDefineProperty(setTimeout, promisify.custom, { __proto__: null, enumerable: true, get() { return setTimeoutPromise; }, }); function clearTimeout(timeout?: Timeout | number) { if (timeout == null) { return; } if (mockTimers !== null && mockTimers._apiEnabled("setTimeout")) { mockTimers._clearTimer(timeout); return; } const id = +timeout; getActiveTimer(id)?.[kDestroy](); } function setInterval( callback: (...args: unknown[]) => void, timeout?: number, ...args: unknown[] ) { validateFunction(callback, "callback"); if (mockTimers !== null && mockTimers._apiEnabled("setInterval")) { return mockTimers._setInterval(callback, timeout, args); } return new Timeout(callback, timeout, args, true, true); } function clearInterval(timeout?: Timeout | number | string) { if (timeout == null) { return; } if (mockTimers !== null && mockTimers._apiEnabled("setInterval")) { mockTimers._clearTimer(timeout); return; } const id = +timeout; getActiveTimer(id)?.[kDestroy](); } function setImmediate( cb: (...args: unknown[]) => void, ...args: unknown[] ): Timeout { validateFunction(cb, "callback"); if (mockTimers !== null && mockTimers._apiEnabled("setImmediate")) { return mockTimers._setTimeout(cb, 0, args, true); } return new Immediate(cb, ...new SafeArrayIterator(args)); } function setImmediatePromise<T = void>( value?: T, options: TimerOptions = kEmptyObject, ): Promise<T> { try { validateObject(options, "options"); if (typeof options?.signal !== "undefined") { validateAbortSignal(options.signal, "options.signal"); } if (typeof options?.ref !== "undefined") { validateBoolean(options.ref, "options.ref"); } } catch (err) { return PromiseReject(err); } const { signal, ref = true } = options; if (signal?.aborted) { return PromiseReject(new AbortError(undefined, { cause: signal.reason })); } let oncancel: EventListenerOrEventListenerObject | undefined; const { promise, resolve, reject } = PromiseWithResolvers(); const immediate = mockTimers !== null && mockTimers._apiEnabled("setImmediate") ? mockTimers._setTimeout(() => resolve(value), 0, [], true) : new Immediate(() => resolve(value)); if (!ref) { immediate.unref(); } if (signal) { oncancel = FunctionPrototypeBind( cancelListenerHandler, immediate, clearImmediate, reject, signal, ); signal.addEventListener("abort", oncancel, { __proto__: null, [lazyEventTarget().kResistStopPropagation]: true, }); } return oncancel !== undefined ? SafePromisePrototypeFinally( promise, () => signal!.removeEventListener("abort", oncancel), ) : promise; } ObjectDefineProperty(setImmediatePromise, "name", { __proto__: null, value: "setImmediate", }); ObjectDefineProperty(setImmediate, promisify.custom, { __proto__: null, enumerable: true, get() { return setImmediatePromise; }, }); function clearImmediate(immediate: Immediate) { if (immediate == null) { return; } if (mockTimers !== null && mockTimers._apiEnabled("setImmediate")) { mockTimers._clearTimer(immediate); return; } if (!immediate?._onImmediate || immediate._destroyed) { return; } core.clearImmediate(immediate); } async function* setIntervalAsync( after: number, value: number, options: { signal?: AbortSignal; ref?: boolean } = { __proto__: null }, ) { validateObject(options, "options"); if (typeof options?.signal !== "undefined") { validateAbortSignal(options.signal, "options.signal"); } if (typeof options?.ref !== "undefined") { validateBoolean(options.ref, "options.ref"); } const { signal, ref = true } = options; if (signal?.aborted) { throw new AbortError(undefined, { cause: signal?.reason }); } let onCancel: (() => void) | undefined = undefined; let interval: Timeout | undefined = undefined; try { let notYielded = 0; let callback: ((value?: object) => void) | undefined = undefined; let rejectCallback: ((message?: string) => void) | undefined = undefined; const onInterval = () => { notYielded++; if (callback) { callback(); callback = undefined; rejectCallback = undefined; } }; interval = mockTimers !== null && mockTimers._apiEnabled("setInterval") ? mockTimers._setInterval(onInterval, after, []) : new Timeout(onInterval, after, [], true, ref); if (signal) { onCancel = () => { clearInterval(interval); if (rejectCallback) { rejectCallback(signal.reason); callback = undefined; rejectCallback = undefined; } }; signal.addEventListener("abort", onCancel, { once: true }); } while (!signal?.aborted) { if (notYielded === 0) { await new Promise((resolve: () => void, reject: () => void) => { callback = resolve; rejectCallback = reject; }); } for (; notYielded > 0; notYielded--) { yield value; } } throw new AbortError(undefined, { cause: signal?.reason }); } catch (error) { if (signal?.aborted) { throw new AbortError(undefined, { cause: signal?.reason }); } throw error; } finally { if (interval) { clearInterval(interval); } if (onCancel) { signal?.removeEventListener("abort", onCancel); } } } const promises = { setTimeout: setTimeoutPromise, setImmediate: setImmediatePromise, setInterval: setIntervalAsync, }; // There is exactly one `scheduler`, so identity is the check: a // prototype-linked object, or one carrying whatever properties the real // instance has, is still a foreign receiver. function validateScheduler(self: unknown) { if (self !== scheduler) { throw new ERR_INVALID_THIS("Scheduler"); } } class Scheduler { constructor() { throw new ERR_ILLEGAL_CONSTRUCTOR(); } // Not `async`: the `this` check has to reject a foreign receiver // synchronously rather than returning a rejected promise. wait( delay: number, options?: { signal?: AbortSignal }, ): Promise<void> { validateScheduler(this); return setTimeoutPromise(delay, undefined, options); } yield() { validateScheduler(this); return promises.setImmediate(); } } const scheduler = ObjectCreate(Scheduler.prototype); promises.scheduler = scheduler; return { setTimeout, clearTimeout, setInterval, clearInterval, setImmediate, clearImmediate, promises, // Internal entry point for `node:test`'s `mock.timers`. Keyed by a symbol so // it does not surface as a public `node:timers` export. Passing a MockTimers // instance enables interception; passing `null` disables it. [kInstallMockTimers]: (instance) => { mockTimers = instance; }, }; })();