前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >nodejs之next_tick源码分析

nodejs之next_tick源码分析

作者头像
theanarkh
发布2019-03-19 14:40:21
7980
发布2019-03-19 14:40:21
举报
文章被收录于专栏:原创分享

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

代码语言:javascript
复制
bootstrap_node.js
    NativeModule.require('internal/process/next_tick').setup();
代码语言:javascript
复制
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函数是操作链表的函数。

代码语言:javascript
复制
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函数里。

代码语言:javascript
复制
 const [
    tickInfo,
    runMicrotasks
  ] = process._setupNextTick(_tickCallback);

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

代码语言:javascript
复制
  env->SetMethod(process, "_setupNextTick", SetupNextTick);

核心代码是

代码语言:javascript
复制
env->set_tick_callback_function(args[0].As<Function>());

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

代码语言:javascript
复制
void InternalCallbackScope::Close {
    env_->tick_callback_function()
}

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

代码语言:javascript
复制
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里执行宏任务的。

代码语言:javascript
复制
Promise.resolve().then(() => {console.log(3)})
process.nextTick(() => {
    console.log(1);
    process.nextTick(() => {
        console.log(11);
        process.nextTick(() => {
            console.log(111);
        })
    })
})

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

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2019-03-14,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 编程杂技 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档