当前位置: 首页 > 工具软件 > Tick Tock > 使用案例 >

nodejs之next_tick源码分析

狄安歌
2023-12-01

next_tick函数是process对象的一个属性。他是在bootstrap_node.js中设置的。

bootstrap_node.js
	NativeModule.require('internal/process/next_tick').setup();
internal/process/next_tick
	process.nextTick = nextTick;
	  function nextTick(callback) {
    if (typeof callback !== 'function')
      throw new errors.TypeError('ERR_INVALID_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];
    }

    nextTickQueue.push(new TickObject(callback, args,
                                      getDefaultTriggerAsyncId()));
  }
	
    class TickObject {
    constructor(callback, args, triggerAsyncId) {
      // this must be set to null first to avoid function tracking
      // on the hidden class, revisit in V8 versions after 6.2
      this.callback = null;
      this.callback = callback;
      this.args = args;

      const asyncId = ++async_id_fields[kAsyncIdCounter];
      this[async_id_symbol] = asyncId;
      this[trigger_async_id_symbol] = triggerAsyncId;

      if (async_hook_fields[kInit] > 0) {
        emitInit(asyncId,
                 'TickObject',
                 triggerAsyncId,
                 this);
      }
    }
  }
 const nextTickQueue = {
    head: null,
    tail: null,
    push(data) {
      const entry = { data, next: null };
      if (this.tail !== null) {
        this.tail.next = entry;
      } else {
        this.head = entry;
        tickInfo[kHasScheduled] = 1;
      }
      this.tail = entry;
    },
    shift() {
      if (this.head === null)
        return;
      const ret = this.head.data;
      if (this.head === this.tail) {
        this.head = this.tail = null;
        tickInfo[kHasScheduled] = 0;
      } else {
        this.head = this.head.next;
      }
      return ret;
    }
  };

我们看到执行next_tick的时候,其实就是把上下文保存到一个链表里。那我们看看什么时候会取出该链表的节点里的函数执行。首先我们看到_tickCallback函数是操作链表的函数。

function _tickCallback() {
    let tock;
    do {
      while (tock = nextTickQueue.shift()) {
        const asyncId = tock[async_id_symbol];
        emitBefore(asyncId, tock[trigger_async_id_symbol]);
        if (async_hook_fields[kDestroy] > 0)
          emitDestroy(asyncId);

        const callback = tock.callback;
        if (tock.args === undefined)
          callback();
        else
          Reflect.apply(callback, undefined, tock.args);

        emitAfter(asyncId);
      }
      runMicrotasks();
    } while (nextTickQueue.head !== null || emitPromiseRejectionWarnings());
    tickInfo[kHasPromiseRejections] = 0;
  }

那他什么时候会执行呢?答案在process._setupNextTickl函数里。

 const [
    tickInfo,
    runMicrotasks
  ] = process._setupNextTick(_tickCallback);

而_setupNextTickl函数是在node.cc定义的一个挂载在process对象上的函数。

  env->SetMethod(process, "_setupNextTick", SetupNextTick);

核心代码是

env->set_tick_callback_function(args[0].As<Function>());

set_tick_callback_function是在env中用宏定义的一个函数。他保存了一个回调函数。被保存的函数会在下面的时机被执行。

void InternalCallbackScope::Close {
	env_->tick_callback_function()
}

该函数是在node.cc定义的。我们看一下什么时候会执行该函数。我们发现只有在node.cc的InternalMakeCallback函数里定义了该类的一个对象。

MaybeLocal<Value> InternalMakeCallback(...) {
	
  InternalCallbackScope scope(env, recv, asyncContext);
  ...
   scope.Close();
}

我们还发现只有两个地方调用了InternalMakeCallback。一个是node.cc一个是async_wrapper.cc。对应的都是MakeCallback函数。因为nodejs里很多c++类都继承了async_wrapper.cc里的AsyncWrap类。然后libuv执行nodejs的c++层回调后,c++层是通过MakeCallback执行js层的回调的。所以我们就可以知道,每次libuv执行上层回调的之后,都会执行next_tick注册的函数。执行完之后会执行runMicrotasks()函数,该函数就是v8里执行宏任务的。

Promise.resolve().then(() => {console.log(3)})
process.nextTick(() => {
    console.log(1);
    process.nextTick(() => {
        console.log(11);
        process.nextTick(() => {
            console.log(111);
        })
    })
})

所以我们能上面的代码中Promise是最后输出的,因为他是微任务。

 类似资料: