前言
hello,我是🥕,今天的文章简单随笔好阅读(landexie) 🌝
附上文地址: 【schedule 系列 - 由React引出的MessageChannel执行时机问题🌝】
在上文中我们探讨了 MessageChannel 在事件循环中的调度,顺便在 schedule 源码中看到不同运行环境中,requestIdleCallback 的模拟不一样。node|old IE 下优先使用 setImmediate,浏览器|高版本node中使用 MessageChannel,否则用 setTimeout 代替。
if (typeof localSetImmediate === 'function') {
// Node.js and old IE.
// There's a few reasons for why we prefer setImmediate.
//
// Unlike MessageChannel, it doesn't prevent a Node.js process from exiting.
// (Even though this is a DOM fork of the Scheduler, you could get here
// with a mix of Node.js 15+, which has a MessageChannel, and jsdom.)
// https://github.com/facebook/react/issues/20756
//
// But also, it runs earlier which is the semantic we want.
// If other browsers ever implement it, it's better to use it.
// Although both of these would be inferior to native scheduling.
schedulePerformWorkUntilDeadline = function () {
localSetImmediate(performWorkUntilDeadline);
};
} else if (typeof MessageChannel !== 'undefined') {
// DOM and Worker environments.
// We prefer MessageChannel because of the 4ms setTimeout clamping.
var channel = new MessageChannel();
var port = channel.port2;
channel.port1.onmessage = performWorkUntilDeadline;
schedulePerformWorkUntilDeadline = function () {
port.postMessage(null);
};
} else {
// We should only fallback here in non-browser environments.
schedulePerformWorkUntilDeadline = function () {
localSetTimeout(performWorkUntilDeadline, 0);
};
}
不得不说优秀团队的代码可读性好到不行,webpack 和 React 中的注释都简洁明了恰到好处。在第一种情况中,注释明确说明了为什么用 setImmediate 代替 messageChannel 的原因:
- 与 MessageChannel 不同,它不会阻止Node.js进程退出
- setImmediate 会更早执行
尽管言简意赅,对于 node 并没有了如指掌的我还是重新复习了 node 的事件循环模型,加深了对上述注释的理解。行きましょう~
node Eventloop
回顾一下事件循环模型:
(图片来源于node官网)
timer 处于第一个阶段,setImmediate 处于 check 阶段,其他常见的 I/O 事件大多在 poll 阶段执行,MessageChannel 同样也在这个阶段。 poll 阶段的特点之一是遍历所有回调并且同步执行,直至到达硬性限制状态(hard limit)。在 Node.js 中使用 MessageChannel 时,可能会创建一个长期存在的异步操作,如果这些异步操作没有被适当地管理或关闭,可能会阻止 Node.js 进程退出,因为进程会等待所有活动的异步操作完成后再退出。
关于注释中的第二个原因,setImmediate 会更早执行,也要结合 poll 阶段的另一个特点理解:poll 队列为空且有脚本被 setImmediate() 调度时,事件循环将结束 poll 阶段,立即执行 check 阶段被setImmediate 调度的任务。同时对比 setTimeout,在同一个 I/O 循环或 timer 内 setImmediate 总是优先执行。这是因为两者同时加入各自队列情况下,setImmediate 在本次事件循环的末尾执行,而 timer 要等到下次执行。
(图片来源于node官网)
总结
别看 node 的事件循环模块这么多,先掌握 timers、poll、check 三个阶段已经可以理解大部分现象并把使用 api 做常规开发了。
本文不是干货,仅仅在 React 注释基础上加深了理解。
参考
- Event Loop, Timers, and process.nextTick():nodejs.org/en/learn/as…
- Don't Block the Event Loop (or the Worker Pool):nodejs.org/en/guides/d…
- The Node.js Event Loop:blog.platformatic.dev/the-nodejs-…
- Node.js 事件循环、定时器、process.nextTick():zhuanlan.zhihu.com/p/461073533
- 谈谈node架构中的线程进程的应用场景、事件循环及任务队列:juejin.cn/post/729334…