Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- 'use strict';
- const { FunctionPrototype } = primordials;
- const {
- // 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,
- triggerFatalException
- } = internalBinding('task_queue');
- const {
- setHasRejectionToWarn,
- hasRejectionToWarn,
- listenForRejections,
- processPromiseRejections
- } = require('internal/process/promises');
- const {
- getDefaultTriggerAsyncId,
- newAsyncId,
- initHooksExist,
- destroyHooksExist,
- emitInit,
- emitBefore,
- emitAfter,
- emitDestroy,
- symbols: { async_id_symbol, trigger_async_id_symbol }
- } = require('internal/async_hooks');
- const {
- ERR_INVALID_CALLBACK,
- ERR_INVALID_ARG_TYPE
- } = require('internal/errors').codes;
- const FixedQueue = require('internal/fixed_queue');
- // *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()) {
- const asyncId = tock[async_id_symbol];
- emitBefore(asyncId, tock[trigger_async_id_symbol]);
- // emitDestroy() places the async_id_symbol into an asynchronous queue
- // that calls the destroy callback in the future. It's called before
- // calling tock.callback so destroy will be called even if the callback
- // throws an exception that is handled by 'uncaughtException' or a
- // domain.
- // TODO(trevnorris): This is a bit of a hack. It relies on the fact
- // that nextTick() doesn't allow the event loop to proceed, but if
- // any async hooks are enabled during the callback's execution then
- // this tock's after hook will be called, but not its destroy hook.
- if (destroyHooksExist())
- emitDestroy(asyncId);
- const callback = tock.callback;
- if (tock.args === undefined)
- callback();
- else
- callback(...tock.args);
- emitAfter(asyncId);
- }
- setHasTickScheduled(false);
- runMicrotasks();
- } while (!queue.isEmpty() || processPromiseRejections());
- setHasRejectionToWarn(false);
- }
- class TickObject {
- constructor(callback, args, triggerAsyncId) {
- this.callback = callback;
- this.args = args;
- const asyncId = newAsyncId();
- this[async_id_symbol] = asyncId;
- this[trigger_async_id_symbol] = triggerAsyncId;
- if (initHooksExist()) {
- emitInit(asyncId,
- 'TickObject',
- triggerAsyncId,
- this);
- }
- }
- }
- // `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) {
- if (typeof callback !== 'function')
- throw new ERR_INVALID_CALLBACK(callback);
- if (process._exiting)
- return;
- var 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 (var i = 1; i < arguments.length; i++)
- args[i - 1] = arguments[i];
- }
- if (queue.isEmpty())
- setHasTickScheduled(true);
- queue.push(new TickObject(callback, args, getDefaultTriggerAsyncId()));
- }
- let AsyncResource;
- function createMicrotaskResource() {
- // Lazy load the async_hooks module
- if (!AsyncResource) {
- AsyncResource = require('async_hooks').AsyncResource;
- }
- return new AsyncResource('Microtask', {
- triggerAsyncId: getDefaultTriggerAsyncId(),
- requireManualDestroy: true,
- });
- }
- function runMicrotask() {
- this.runInAsyncScope(() => {
- const callback = this.callback;
- try {
- callback();
- } catch (error) {
- // TODO(devsnek): Remove this if
- // https://bugs.chromium.org/p/v8/issues/detail?id=8326
- // is resolved such that V8 triggers the fatal exception
- // handler for microtasks.
- triggerFatalException(error, false /* fromPromise */);
- } finally {
- this.emitDestroy();
- }
- });
- }
- function queueMicrotask(callback) {
- if (typeof callback !== 'function') {
- throw new ERR_INVALID_ARG_TYPE('callback', 'function', callback);
- }
- const asyncResource = createMicrotaskResource();
- asyncResource.callback = callback;
- enqueueMicrotask(FunctionPrototype.bind(runMicrotask, asyncResource));
- }
- module.exports = {
- setupTaskQueue() {
- // Sets the per-isolate promise rejection callback
- listenForRejections();
- // Sets the callback to be run in every tick.
- setTickCallback(processTicksAndRejections);
- return {
- nextTick,
- runNextTicks
- };
- },
- queueMicrotask
- };
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement