node中我对Event Loop和宏任务,微任务的理解

本文深入探讨JavaScript的异步执行机制,包括任务队列、主线程、宏任务和微任务的概念,通过具体示例解析执行顺序及原因,帮助读者理解setTimeout、Promise、process.nextTick等异步函数的行为。

 

 开始理论部分

1.任务队列和主线程

任务分为两种,一种同步任务,一种是异步任务。同步任务会在主线程执行

同步任务指的是,在主线程上排队执行的任务,只有前一个任务执行完毕,才能执行后一个任务;异步任务指的是,不进入主线程、而进入"任务队列"(task queue)的任务,只有"任务队列"通知主线程,某个异步任务可以执行了,该任务才会进入主线程执行。

只要主线程空了,就会去读取"任务队列",这就是JavaScript的运行机制。这个过程会不断重复。

那又什么是宏任务和微任务呢,单拿出来同步任务,也分宏任务和微任务。执行顺序就是先执行宏任务,再执行微任务。

在异步队列里是不是也有宏任务和微任务,那是当然,执行顺序也是一样。现在我们只要记住谁是宏任务,谁是微任务,谁在前谁在后就行了

宏任务

一般普通的代码都是宏任务,一步一步往下执行。for循环,声明变量,声明函数等等

 

#浏览器Node
setTimeout
setInterval
setImmediatex
requestAnimationFramex

 

微任务

#浏览器Node
process.nextTickx
MutationObserverx
Promise.then catch finally

 

 

我下边举个例子,一边看这个例子,一边看我上边列出来那个宏任务和微任务那个表

setTimeout(() => {
    //执行后 回调一个宏事件
    console.log(1)
}, 0)
console.log(2);

new Promise((resolve) => {
    console.log(3);
    resolve()
}).then(() => {
    console.log(4);
}).then(()=>{
    console.log(5);
})
//node执行结果 2 3 4 5 1

首先千万不要把异步队列和这个宏任务微任务搞混,认为异步的就微任务,那是没理解透。

分析上边代码。

1. 代码从上往下执行,先遇见一个setTimeout,一看是个微任务,你先等等,看看有没有宏任务要执行。
 2. 发现有,console.log(2);他是这个代码最外层中唯一宏任务,不用考虑,执行掉。
 ------->  输出 2
 3. 剩下一个Promise和setTimeout,都是微任务,谁在前边谁先执行。
4. 解析到setTimeout,发现是个异步任务,就给他扔到异步队列里去了,等我们主线程完事再管你。
 5. 然后就是Promise执行了,这个console.log(3);就被执行了。  
  -------->  输出  3
 6. 当程序发现了then,这又是个异步任务啊。那你也去异步队列里去排队去。
 7. 这个时候你会想,我懂了。然后就是1,4,5,可是结果却是4,5,1 //  console.log(1)这个时候有想法了,怎么的我们都是console.log你俩凭啥在我前边,而且我就算是排队也是我先输出啊
 这个问题出在这里。异步队列中的这个setTimeout里的回调虽然排队排第一个了,但是时间还没到。
HTML5标准规定了setTimeout()的第二个参数的最小值(最短间隔),不得低于4毫秒,如果低于这个值,就会自动增加
也即是说,第一次主线程空了去轮询异步队列时,setTimeout时间未到.也就是说看着是0实际上还是有时间的,所以就出现这个问题了

------>继续 输出4 ,5 , 1

除了setTimeout和setInterval这两个方法
Node.js还提供了另外两个与任务队列有关的方法:process.nextTick和setImmediate。
process.nextTick方法可以在当前"执行栈"的尾部----下一次Event Loop(主线程读取"任务队列")之前----触发回调函数。
也就是说,它指定的任务总是发生在所有异步任务之前。
setImmediate方法则是在当前"任务队列"的尾部添加事件,也就是说,它指定的任务总是在下一次Event Loop时执行

so利用下边的案例,就能彻底解决疑惑


console.log('1');
setTimeout(function() {
    console.log('2');
    process.nextTick(function() {
        console.log('3');
    })
    new Promise(function(resolve) {
        console.log('4');
        resolve();
    }).then(function() {
        console.log('5')
    })
})

process.nextTick(function() {
    console.log('6');
})

new Promise(function(resolve) {
    console.log('7');
    resolve();
}).then(function() {
    
    console.log('8')
})

setTimeout(function() {
    console.log('9');
    process.nextTick(function() {
        console.log('10');
    })
    new Promise(function(resolve) {
        console.log('11');
        resolve();
    }).then(function() {
        console.log('12')
    })
})

先公布答案:

执行结果是:1,7,6,8,2,4,9,11,3,10,5,12

// 执行结果是:1,7,6,8,2,4,9,11,3,10,5,12
// 分析:  1. 首先看,主程序,先执行宏任务,console.log(1);
// -------  1
// 2. 然后就是执行微任务 第一个setTimeout回调扔进异步队列等着,process.nextTick当执行异步事件队列的时候第一个执行,然后Promise执行
// -------  7
// 3. 然后.then扔进异步时间队列,然后遇到setTimeout,再次扔进队列
// 4. 主进程执行完毕,执行异步时间队列,所以process.nextTick下边的6被执行
// -------  6
// 然后执行then里的内容,因为setTimeout的时间还没到。(前边有讲过为什么)
// -------  8
// 5. 然后第一个setTimeout里的回调开始执行,也分宏任务和微任务的方式执行,同时也是遇到异步就送到异步队列中去。等待主线程完成。
// 6. 也就是说第一个setTimeout里的回调函数里的主线程全部执行完,
// -------- 2
// -------- 4
// 7. 然后主线程没东西了,去异步线程,此时最后一个setTimeout也被推到最前边来了。然后也按宏任务,微任务等等玩法
// -------  9  
// -------- 11
// 剩下的不用再说了。执行异步队列了。按顺序执行就是了。 3,10,5,12

弄懂他,就算是有这类的面试题你也不怂啊

 

<think>嗯,用户想了解JavaScript异步执行机制中的任务任务。首先,我需要回顾一下自己对这个主题的理解,确保准确无误。 JavaScript是单线程的,这意味着它一次只能执行一个任务过,异步机制让它能够处理非阻塞操作,比如网络请求或定时器。而任务任务就是异步处理中的两个重要概念。 记得事件循环Event Loop)是核心,负责管理任务队列。当主线程执行完同步代码后,事件循环会检查任务队列,先处理任务队列中的所有任务,然后再处理任务队列中的一个任务。这个过程断重复。 需要区分任务任务的常见例子。比如,Promise的回调属于任务,而setTimeoutsetInterval属于任务。还有MutationObserver也是任务,而I/O操作、UI渲染可能属于任务。 用户可能需要知道为什么任务优先于任务。比如,在一个事件循环中,任务会在当前任务执行完后立即执行,而任务要等到下一次循环。这会影响代码的执行顺序,尤其是在嵌套的Promise定时器中。 还要注意可能出现的陷阱,比如在任务中创建新的任务,这会导致任务被长时间阻塞。需要提醒用户避免这种情况,以免影响性能。 结构方面,应该先解释基本概念,再讲执行顺序,接着举例说明,最后总结注意事项。使用中文,保持清晰,避免技术术语过于复杂,必要时用代码示例帮助理解。 检查是否有错误,比如是否正确处理了async/await中的任务生成,或者Node.js环境下的差异。过用户可能只需要浏览器环境下的解释,所以暂时Node.js的process.nextTick等细节。 最后确保使用正确的LaTeX格式,比如行内公式用$...$,独立公式用$$...$$,但这个问题可能涉及数学公式,过如果有涉及的地方要注意格式正确。比如在说明执行顺序时,可能需要用时间线或步骤列表,但可能需要数学表达式。 确认回答结构:定义任务任务,执行机制,执行顺序,代码示例,常见问题,注意事项。这样用户能逐步理解。</think>### JavaScript 异步执行机制中的任务任务 #### 一、基本概念 1. **任务(Macro Task)** - 代表代码执行的基本单元,例如: - `setTimeout`、`setInterval` - I/O 操作(如文件读写) - UI 渲染 - 整体 `script` 代码(主线程代码) 2. **任务(Micro Task)** - 优先级高于任务,例如: - `Promise.then()`、`Promise.catch()` - `MutationObserver` - `process.nextTick`(Node.js 环境) --- #### 二、执行机制 JavaScript 通过 **事件循环Event Loop)** 管理异步任务,执行流程如下: 1. **主线程执行同步代码** - 遇到任务(如 `setTimeout`)时,将其回调推入 **任务队列**。 - 遇到任务(如 `Promise.then()`)时,将其回调推入 **任务队列**。 2. **同步代码执行完毕** - 检查任务队列,依次执行所有任务(**直到队列清空**)。 - 执行一个任务(从队列头部取一个任务)。 - 重复上述步骤,形成循环。 --- #### 三、执行顺序示例 ```javascript console.log("1. 主线程开始"); // 同步代码 setTimeout(() => { console.log("4. 任务执行"); }, 0); Promise.resolve().then(() => { console.log("3. 任务执行"); }); console.log("2. 主线程结束"); // 同步代码 ``` **输出顺序**: 1. 主线程开始 → 2. 主线程结束 → 3. 任务执行 → 4. 任务执行 --- #### 四、关键规则 1. **任务优先** - 每执行完一个任务后,会立即清空任务队列。 - 任务中产生的任务会添加到当前队列末尾,**在同一轮循环中执行**。 2. **任务调度** - 浏览器可能合并多个任务(如连续 `setTimeout`)。 --- #### 五、常见问题 **问题**:以下代码的输出顺序是什么? ```javascript setTimeout(() => console.log("任务1"), 0); Promise.resolve().then(() => { console.log("任务1"); Promise.resolve().then(() => console.log("任务2")); }); setTimeout(() => console.log("任务2"), 0); ``` **答案**: 任务1 → 任务2 → 任务1 → 任务2 --- #### 六、注意事项 1. **避免任务阻塞** - 若任务无限递归(如循环添加任务),会导致任务永远无法执行。 2. **浏览器渲染时机** - UI 渲染作为任务执行,频繁任务可能延迟页面更新。 --- ### 总结 - **任务**:优先级高,用于轻量级异步操作(如 Promise 回调)。 - **任务**:优先级低,用于耗时操作(如定时器、I/O)。 - **核心规则**:**同步代码 → 任务任务任务 → ...** 理解这一机制能帮助开发者优化代码执行顺序,避免异步陷阱。
评论 26
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值