ICode9

精准搜索请尝试: 精确搜索
首页 > 编程语言> 文章详细

学习笔记—Node中的EventLoop

2021-11-15 05:31:07  阅读:202  来源: 互联网

标签:Node nextTick EventLoop console log 队列 setImmediate 笔记 执行


日常的学习笔记,包括 ES6、Promise、Node.js、Webpack、http 原理、Vue全家桶,后续可能还会继续更新 Typescript、Vue3 和 常见的面试题 等等。


nextTick

参考文献 JavaScript运行机制详解

首先,process.nextTick是node自己实现的方法,并不属于node中的EventLoop,他的优先级也比promise更高

Promise.resolve().then(() => {
    console.log('promise');
})
process.nextTick(() => {
    console.log('nextTick');
})
// nextTick
// promise

我们可以看到,上述执行队列中,会先输出nextTick,再输出promise

原因就是因为nextTick的执行位置,他会在执行上下文栈执行完毕后,立即执行nextTick

node中的nextTick

Node中的EventLoop

参考文献 Node.js事件循环、定时器和process.nextTick()

为了更好的学习和理解EventLoop与nextTick的关系,我们也可以参考下图。

    本阶段执行已经被 setTimeout() 和 setInterval() 的调度回调函数。
   ┌───────────────────────────┐
┌─>│           timers          │ 
│  └─────────────┬─────────────┘
|   执行延迟到下一个循环迭代的 I/O 回调。
│  ┌─────────────┴─────────────┐
│  │     pending callbacks     │
│  └─────────────┬─────────────┘
|   仅系统内部使用。
│  ┌─────────────┴─────────────┐
│  │       idle, prepare       │
│  └─────────────┬─────────────┘      
|   检索新的I/O事件;执行与 I/O 相关的回调  ┌───────────────┐
│  ┌─────────────┴─────────────┐      │   incoming:   │
│  │           poll            │<─────┤  connections, │
│  └─────────────┬─────────────┘      │   data, etc.  │
│   setImmediate() 回调函数在这里执行。  └───────────────┘
│  ┌─────────────┴─────────────┐      
│  │           check           │
│  └─────────────┬─────────────┘
|  一些关闭的回调函数
│  ┌─────────────┴─────────────┐
└──┤      close callbacks      │  
   └───────────────────────────┘

这里每一个阶段都对应一个事件队列,当 event loop 执行到某个阶段时会将当前阶段对应的队列依次执行。当该队列已用尽或达到回调限制,事件循环将移动到下一阶段。

我们可以通过以下代码来感受一下上述概念。

setTimeout(() => {
    console.log('timeout');
}, 0);

setImmediate(()=>{
    console.log('setImmediate');
});
// timeout
// setImmediate

这里我们可以发现,timeout 会在 setImmediate 之前输出。

但是这里会有一个问题,多次执行之后我们会发现,timeout 也会在 setImmediate 之后输出。

原因是因为当前默认执行主栈代码,主栈代码执行完毕后,再去执行宏任务队列。但是因为我们的定时器设定的时间是0,可能会有几毫秒的延迟,所以当主栈代码执行完毕后,定时器并没有到达指定的执行时间,所以会存在两个结果输出情况不一致的情况。

我们可以称他为 准备阶段时间

假设我们目前存在这样一段代码,从I/O事件中执行setImmediatesetTimeout

fs.readFile('./test.txt', 'utf-8', () => {
    setTimeout(() => {
        console.log('timeout');
    });
    setImmediate(() => {
        console.log('setImmediate');
    })
})
//setImmediate
//timeout

我们的代码在执行时,会先检测是否存在 timer阶段,也就是定时器。没有的话,会去检测 poll队列(I/O事件)是否为空。检测完毕后,再去检测是否存在 setImmediate,也就是 check阶段 。检测完毕后,会进行轮询,也就是下一轮检测。

这样我们可以总结一下

  1. 检测Poll队列中是否为空,如果不为空则执行队列中的任务,直到超时或者全部执行完毕。
  2. 执行完毕后检测setImmediate队列是否为空,如果不为空则执行check阶段,如果为空则等待时间到达。时间到达后回到timer阶段。
  3. 等待时间到达是可能会出现新的callback,此时也在当前阶段被清空。

面试题

搞清楚了Node中的EventLoop,那么EventLoop和微任务的关系又是什么样的呢?

我们可以看一道曾经腾讯的面试题

setTimeout(() => {
    console.log('a');
}, 0);
setImmediate(() => {
    console.log('b');
});
new Promise((resolve) => {
    console.log('c');
    resolve()
}).then(() => {
    console.log('d');
})
console.log('e');
// c e d a b

根据我们之前学过的知识我们可以推导出这道题的结果。

Node在执行时,会先将主执行栈代码执行完毕,这时输出结果 c 、e,将promise.then()放入微任务队列。然后会拿出一个宏任务并执行,先清空微任务队列,这时 输出结果 d。随后再执行定时器回调,输出结果 a 。然后根据Node中EventLoop的执行顺序,timer清空完毕后进入poll阶段,检查是否存在I/O事件。随后检测setImmediate队列,并将 输出结果 b


本篇文章由莫小尚创作,文章中如有任何问题和纰漏,欢迎您的指正与交流。
您也可以关注我的 个人站点博客园掘金,我会在文章产出后同步上传到这些平台上。
最后感谢您的支持!

标签:Node,nextTick,EventLoop,console,log,队列,setImmediate,笔记,执行
来源: https://www.cnblogs.com/moxiaoshang/p/15554287.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有