-
Notifications
You must be signed in to change notification settings - Fork 20
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
node源码粗读(9):nextTick、timers API、MicroTasks注册到执行全阶段解读 #20
Comments
讲的很好!很清楚! |
@liximomo 谢谢 |
setImmediate(() => {
setTimeout(() => console.log(3), 0);
process.nextTick(() => console.log(1));
Promise.resolve().then(() => console.log(2));
}); 上诉代码 bootstrap 后 进入 event-loop. 同样的还有 const fs = require('fs');
fs.readFile('none-exist.js', () => {
process.nextTick(() => console.log(1));
Promise.resolve().then(() => console.log(2));
setImmediate(() => console.log(3));
}); 会在 bootstrap 后 进入首先 event-loop 的 poll 阶段, 按照图示,接下来应该是 check阶段, 执行输出应该是 3 1 2,但实际是 1 2 3。 请问我哪里理解的不对吗?望解答! |
@liximomo 第一个问题,文中有这样一句话:
所以在check阶段执行完之后还会执行相应的回调流程,即nextTick->Microtasks->继续event-loop。 第二个问题:
首先你这句话就错了,fs所在的阶段是poll阶段,I/O callbacks阶段这个I/O主要是提供给libuv内部stream、IPC等用的。而fs中内部调用的是 |
@xtx1130 谢谢!这下清楚了。而且借助您的图我发现 setTimeout 在 node 和 浏览器中行为居然是不一致的!node 里会把 TimerList 里的回调全部执行完才触发 MicroTasks。 而浏览器在调用栈为空的时候就会执行 MicroTasks。 setTimeout(() => {
console.log(1);
Promise.resolve().then(() => console.log(11));
}, 10);
setTimeout(() => {
console.log(2)
}, 10); node 输出
browser 输出
|
整体流程
在这里以下面这段代码为例子,画一下整体的运行流程:
注意:
timers每个阶段timers和check阶段都会执行InternalCallback
以及InternalCallback::Close
,在这里为了简化,只画了timers的流程。poll阶段由于不在本文讨论范围内,不做过多解读。nextTick
以及microTask
相关的流程node源码粗读(6):从./lib/timers.js来看timers相关API底层实现
node源码粗读(7):nextTick和microtasks从bootstrap到event-loop全阶段解读
node源码粗读(8):setImmediate注册+触发全流程解析
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,开始监听,到时间触发运行回调函数:
nextTick在注册之后且bootstrap构建结束后运行
SetupNextTick
函数,从而触发nextTick的运行,而nextTick在运行之后会触发runMicroTasks()
,清空bootstrap阶段的microTask:event-loop阶段
在bootstrap之后便进入了event-loop。event-loop第一个阶段便是timers,在这里如果有到时间的Timer,便会触发
OnTimeout
,OnTimeout
会触发InternalMakeCallback
从而执行TimersList中的函数。而在执行完后还会触发InternalCallbackScope::Close
,在这个函数中会触发nextTick,在触发nextTick后触发microTasks。setTimeout
简易流程如下:也正是
InternalMakeCallback
和InternalCallbackScope::Close
使得libuv和v8紧紧的联系在了一起,一方面可以通过setTimeout
来设置运行时间;另一方面又可以在setTimeout
的回调中书写js代码。by 小菜
The text was updated successfully, but these errors were encountered: