UNPKG

nstdlib-nightly

Version:

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

184 lines (159 loc) 4.92 kB
// Source: https://github.com/nodejs/node/blob/65eff1eb/lib/internal/process/task_queues.js import { // For easy access to the nextTick state in the C++ land, // and to avoid unnecessary calls into JS land. tickInfo, // Used to run V8's micro task queue. runMicrotasks, setTickCallback, enqueueMicrotask, } from "nstdlib/stub/binding/task_queue"; import { setHasRejectionToWarn, hasRejectionToWarn, listenForRejections, processPromiseRejections, } from "nstdlib/lib/internal/process/promises"; import { getDefaultTriggerAsyncId, newAsyncId, initHooksExist, destroyHooksExist, emitInit, emitBefore, emitAfter, emitDestroy, symbols as __symbols__, } from "nstdlib/lib/internal/async_hooks"; import * as FixedQueue from "nstdlib/lib/internal/fixed_queue"; import { validateFunction } from "nstdlib/lib/internal/validators"; import { AsyncResource } from "nstdlib/lib/async_hooks"; import * as AsyncContextFrame from "nstdlib/lib/internal/async_context_frame"; const { async_id_symbol, trigger_async_id_symbol } = __symbols__; const async_context_frame = Symbol("kAsyncContextFrame"); // *Must* match Environment::TickInfo::Fields in src/env.h. const kHasTickScheduled = 0; function hasTickScheduled() { return tickInfo[kHasTickScheduled] === 1; } function setHasTickScheduled(value) { tickInfo[kHasTickScheduled] = value ? 1 : 0; } const queue = new FixedQueue(); // Should be in sync with RunNextTicksNative in node_task_queue.cc function runNextTicks() { if (!hasTickScheduled() && !hasRejectionToWarn()) runMicrotasks(); if (!hasTickScheduled() && !hasRejectionToWarn()) return; processTicksAndRejections(); } function processTicksAndRejections() { let tock; do { while ((tock = queue.shift()) !== null) { const priorContextFrame = AsyncContextFrame.exchange( tock[async_context_frame], ); const asyncId = tock[async_id_symbol]; emitBefore(asyncId, tock[trigger_async_id_symbol], tock); try { const callback = tock.callback; if (tock.args === undefined) { callback(); } else { const args = tock.args; switch (args.length) { case 1: callback(args[0]); break; case 2: callback(args[0], args[1]); break; case 3: callback(args[0], args[1], args[2]); break; case 4: callback(args[0], args[1], args[2], args[3]); break; default: callback(...args); } } } finally { if (destroyHooksExist()) emitDestroy(asyncId); } emitAfter(asyncId); AsyncContextFrame.set(priorContextFrame); } runMicrotasks(); } while (!queue.isEmpty() || processPromiseRejections()); setHasTickScheduled(false); setHasRejectionToWarn(false); } // `nextTick()` will not enqueue any callback when the process is about to // exit since the callback would not have a chance to be executed. function nextTick(callback) { validateFunction(callback, "callback"); if (process._exiting) return; let args; switch (arguments.length) { case 1: break; case 2: args = [arguments[1]]; break; case 3: args = [arguments[1], arguments[2]]; break; case 4: args = [arguments[1], arguments[2], arguments[3]]; break; default: args = new Array(arguments.length - 1); for (let i = 1; i < arguments.length; i++) args[i - 1] = arguments[i]; } if (queue.isEmpty()) setHasTickScheduled(true); const asyncId = newAsyncId(); const triggerAsyncId = getDefaultTriggerAsyncId(); const tickObject = { [async_id_symbol]: asyncId, [trigger_async_id_symbol]: triggerAsyncId, [async_context_frame]: AsyncContextFrame.current(), callback, args, }; if (initHooksExist()) emitInit(asyncId, "TickObject", triggerAsyncId, tickObject); queue.push(tickObject); } function runMicrotask() { this.runInAsyncScope(() => { const callback = this.callback; try { callback(); } finally { this.emitDestroy(); } }); } const defaultMicrotaskResourceOpts = { requireManualDestroy: true }; function queueMicrotask(callback) { validateFunction(callback, "callback"); const asyncResource = new AsyncResource( "Microtask", defaultMicrotaskResourceOpts, ); asyncResource.callback = callback; enqueueMicrotask(Function.prototype.bind.call(runMicrotask, asyncResource)); } const _export_setupTaskQueue_ = function () { // Sets the per-isolate promise rejection callback listenForRejections(); // Sets the callback to be run in every tick. setTickCallback(processTicksAndRejections); return { nextTick, runNextTicks, }; }; export { _export_setupTaskQueue_ as setupTaskQueue }; export { queueMicrotask };