-
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源码粗读(7):nextTick和microtasks从bootstrap到event-loop全阶段解读 #16
Comments
有个地方想请教一下,node 的官方文档写明event loop分为timer、io callbacks 、poll几个phrase,在code.c的 |
@youth7 这篇文章提过一句:
通过这篇文章和上一篇文章能关联出来:上篇文章中说过 |
@xtx1130
有点不符合,不知道我是错过了什么地方 |
(下面的表述是基于 |
你好,麻烦问下
这段逻辑为什么run next tick和microtask run会在uv_run之前执行呢?Node官网也有说明,在event loop前会清空next_tick队列,但是在源码上没有找到 |
@tsy77 在bootstrap阶段触发就是通过 |
@xtx1130 了解了 感谢! |
nextTick实现
目光直接转移到next_tick.js,整体nextTick的代码其实很容易理解:
通过这两个函数,就能看出来整个nextTick是如何工作的。
大家注意一下
process._setupNextTick(_tickCallback)
,最终这个_tickCallback并没有在js中执行,而是传递给了c++:在这里可以看出来,最终_tickCallback丢给了
tick_callback_function
,然后在LoadEnvironment
中通过_setupNextTick
触发运行(LoadEnvironment
之前详细介绍过,在这里不做过多介绍),在这里简单的追踪了一下_tickCallback来证实一下最终_tickCallback传递给了tick_callback_function
:tips: 蓝色底色代码为断点所在位置,下方为此时刻的内存地址,上面这张图可以看出来在没有跑
LoadEnvironment
的时候,tick_callback_function
为NULL如果对
LoadEnvironment
比较了解的读者,应该是明白其中的原理的,如果不明白原理可以简单看一下tick_callback_function
这里的内存变化。这里我们假设读者了解node启动的所有机制,那么就会发现一件事情:在process.nextTick
运行的时候,uv_run
尚未启动。那么,我们可以根据这个显现得出一个比较浅显的结论:
process.nextTick
会阻塞libuv的事件循环。(这是在node初始化bootatrap阶段的情况。即使在evnt_loop中,表现也是一样的。为何用这个阶段来叙述,是因为这个阶段最容易追踪和解读)process.nextTick和RunMicrotasks
通过前一章节的叙述和上一篇文章对setTimeout流程的分析,我们可以发现:
process.nextTick
不是基于libuv事件机制的,而timers一系列的api全部是基于libuv开放出来的api实现的。那么这个nextTick到底是如何实现的呢?接下来就要从nextTick的源码聊起了:
在执行完nextTick之后(
callback()
)还继续执行了runMicrotasks
,我相信如果了解过Microtasks的读者肯定知道这到底是做什么的,接下来我们深扒一下这个runMicrotasks
:通过上面的代码,可以比较清晰地看到整个
RunMicrotasks
的全过程,主要就是通过microtask_queue来实现的Microtask。了解了整个流程,可以很容易得出一个结论:nextTick会在v8执行Microtasks之前对在js中注册的nextTickQueue逐个执行,即阻塞了Microtasks执行。
bootstrap阶段和event-loop时候的异同
通过上面的分析,下面这段代码在bootstrap阶段,应该很容易理解:
结果如图:
相关解释已经写到了上面的注释中。 (当然这里用console来作为同步代码不是很严谨,不过比较直观)
那么在event-loop中是如何表现的呢?在上文中也提到过一句:
event-loop中的区别是:本应该在node LoadEnvironment(bootstrap)阶段执行的代码的运行转移到了
InternalMakeCallback
中。下面是
InternalMakeCallback
的代码:通过
ret = callback->Call(env->context(), recv, argc, argv);
实现了event-loop中主体代码的运行,之后在InternalMakeCallback结束之后,实现对nextTick和microtask的调用,代码如下:其中,有两个需要注意的地方,一个是:
这两处代码专门针对无process.nextTick行为的event-loop进行了处理,直接从node中调用v8的RunMicrotasks,加快整体处理速度。
另外一个地方是:
通过对tick_callback_function的调用,实现触发之前讲过的
_tickCallback
,不知道大家还记得这句话么:这样,整体形成了一个闭环,无论是bootstrap阶段还是在event-loop阶段,总是能保证两点:
by 小菜
The text was updated successfully, but these errors were encountered: