一、概述
JavaScrip是单线程的,也就意味着同一时间只能处理一件事,如果其中一件事运行非常耗时,就会阻塞线程。
Event Loop 即事件循环, 就是JavaScrip为解决单线程代码执行不阻塞主进程一种机制,也就是异步原理。事件循环是实现异步执行的核心机制之一。
二、事件循环(Event Loop)
事件循环机制其实分为浏览器事件循环和Node.js事件循环
事件队列中的任务分为宏任务和微任务
宏任务(task):指由浏览器发起的任务,比如整体的同步代码、setTimeout、setInterval、setImmediate 、 MessageChannel,requestAnimationFrame,I/O,用户交互,网络请求等;
微任务(Microtask ):指在当前任务task执行结束后立即执行的任务,比如Promise.then()回调、Promise.catch()回调、Promise.resolve()、process.next()、MutationObserver等;
理解:
- 在异步队列中会先找到微任务,执行完所有微任务后,执行下一个宏任务
- 每一个宏任务执行完之后,都会检查是否存在待执行的微任务
- 如果有,则执行完所有微任务之后,再继续执行下一个宏任务
在JavaScript中,所有的同步代码执行都被视为一个宏任务。当浏览器执行JavaScript代码时,它会创建一个主执行线程,其中包含一个主宏任务队列。同步代码会按照顺序执行,形成一个宏任务。这个宏任务在执行期间,可以包含其他的异步任务,比如通过setTimeout、setInterval、事件处理器等触发的任务。
当主宏任务执行完毕后,浏览器会检查微任务队列,执行其中的微任务。微任务是一种特殊的异步任务,它们具有更高的优先级,会在宏任务执行结束后立即执行。典型的微任务包括Promise的回调和MutationObserver的回调。
需要注意:new Promise()是同步方法,Promise.resolve()、Promise.reject()、Promise.then()、Promise.catch()回调才是异步微任务;
一个简单的浏览器事件循环过程:
执行同步代码: 从上到下按照顺序执行当前执行栈中的同步代码,直到执行栈为空。
执行微任务队列: 在执行栈清空后,立即执行微任务队列中的所有微任务。
更新渲染: 如果在浏览器环境中,此时浏览器会检查是否需要更新页面的渲染。
执行宏任务: 从宏任务队列中取出一个任务执行,这个任务被称为宏任务(Macro Task)。
添加新的宏任务: 如果在执行宏任务的过程中产生了新的宏任务,将其添加到宏任务队列中等待执行。
重复: 重复上述步骤,不断循环执行,直到没有待执行的任务。
简单示例
console.log('Start');
setTimeout(() => {
console.log('Timeout');
}, 0);
Promise.resolve().then(() => {
console.log('Promise');
});
console.log('End');
输出顺序:Start 、 End、Promise、Timeout
输出解析:
- 同步代码执行,输出 Start 和 End。
- 微任务队列中的Promise回调执行,输出 Promise。
- 宏任务队列中的setTimeout回调执行,输出 Timeout