Skip to content

node源码粗读(9):nextTick、timers API、MicroTasks注册到执行全阶段解读 #20

@xtx1130

Description

@xtx1130

本文主要介绍nextTick、timers API、MicroTasks几类任务是在什么时候注册和执行的,也会从node 的bootstrap到evnet-loop过程做一个简单的介绍

整体流程

在这里以下面这段代码为例子,画一下整体的运行流程:

setTimeout(() => console.log('timers API'), 10)
new Promise((resolve, reject) => resolve('microtask run')).then(arg => console.log(arg))
process.nextTick(() => console.log('run next tick'))
setImmediate(() => console.log('setImmediate API'))

issue20-1
注意:

Environment::Start

在这里不做过多详细介绍了,之前的文章中介绍过很多。有一个知识点需要注意就是:Immediate是在这个阶段注册到uv_check_start中的。此Immediate其实是Environment::CheckImmediate函数,保证之后event-loop在运行的时候能在check阶段运行这个函数,进而触发immediate_callback_function实现对ImmediateList的调用。
至于idle部分,是为了在有ImmediateList的时候直接跳过poll阶段,毕竟poll是阻塞运行的。

bootstrap阶段

bootstrap阶段是node运行时候的构建阶段,也是最基础的阶段。bootstrap阶段会把整体的node架子搭起来(在这里不做详细介绍),之后运行业务代码。比如本文中的这个例子,上图中画的应该也比较清晰了,顺序执行。唯一的区别是:在执行不同API的时候,callback的去向是不一致的。从上图中也能看出来,这几个API最终的去向分别是:TimersList、microTask、immediateQueue、nextTickQueue。
其中,setTimeout在注册的时候会创建TimerWrap的实例,在创建实例的时候会初始化uv_timer,之后再通过TimerWrap.start启动uv_timer_start,开始监听,到时间触发运行回调函数:
issue20-2
nextTick在注册之后且bootstrap构建结束后运行SetupNextTick函数,从而触发nextTick的运行,而nextTick在运行之后会触发runMicroTasks(),清空bootstrap阶段的microTask:
issue20-3

event-loop阶段

在bootstrap之后便进入了event-loop。event-loop第一个阶段便是timers,在这里如果有到时间的Timer,便会触发OnTimeoutOnTimeout会触发InternalMakeCallback从而执行TimersList中的函数。而在执行完后还会触发InternalCallbackScope::Close,在这个函数中会触发nextTick,在触发nextTick后触发microTasks。setTimeout简易流程如下:
issue20-4
也正是InternalMakeCallbackInternalCallbackScope::Close使得libuv和v8紧紧的联系在了一起,一方面可以通过setTimeout来设置运行时间;另一方面又可以在setTimeout的回调中书写js代码。

by 小菜

Metadata

Metadata

Assignees

No one assigned

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions