一次搞懂- JS 事件循环之宏任务和微任务
SegmentFault
共 5467字,需浏览 11分钟
·
2021-05-11 20:31
作者:九旬
来源:SegmentFault 思否社区
众所周知,JS 是一门单线程语言,可是浏览器又能很好的处理异步请求,那么到底是为什么呢?
JS 的执行环境一般是浏览器和 Node.js,两者稍有不同,这里只讨论浏览器环境下的情况。
JS 执行过程中会产生两种任务,分别是:同步任务和异步任务。
同步任务:比如声明语句、for、赋值等,读取后依据从上到下从左到右,立即执行。 异步任务:比如 ajax 网络请求,setTimeout 定时函数等都属于异步任务。异步任务会通过任务队列(Event Queue)的机制(先进先出的机制)来进行协调。
任务队列(Event Queue)
宏任务主要包括:scrip(JS 整体代码)、setTimeout、setInterval、setImmediate、I/O、UI 交互 微任务主要包括:Promise(重点关注)、process.nextTick(Node.js)、MutaionObserver
理解微任务和宏任务的执行执行过程
console.log("script start");
setTimeout(function () {
console.log("setTimeout");
}, 0);
Promise.resolve()
.then(function () {
console.log("promise1");
})
.then(function () {
console.log("promise2");
});
console.log("script end");
宏任务:执行整体代码(相当于 <script>
中的代码):输出: script start
遇到 setTimeout,加入宏任务队列,当前宏任务队列(setTimeout) 遇到 promise,加入微任务,当前微任务队列(promise1) 输出: script end
微任务:执行微任务队列(promise1) 输出: promise1
,then 之后产生一个微任务,加入微任务队列,当前微任务队列(promise2)执行 then,输出 promise2
执行渲染操作,更新界面(敲黑板划重点)。 宏任务:执行 setTimeout 输出: setTimeout
Promise 的执行
new Promise(..)
中的代码,也是同步代码,会立即执行。只有then
之后的代码,才是异步执行的代码,是一个微任务。console.log("script start");
setTimeout(function () {
console.log("timeout1");
}, 10);
new Promise((resolve) => {
console.log("promise1");
resolve();
setTimeout(() => console.log("timeout2"), 10);
}).then(function () {
console.log("then1");
});
console.log("script end");
当前任务队列:微任务: [], 宏任务:[ <script>
]
宏任务: 输出: script start
遇到 timeout1,加入宏任务 遇到 Promise,输出 promise1
,直接 resolve,将 then 加入微任务,遇到 timeout2,加入宏任务。输出 script end
宏任务第一个执行结束
当前任务队列:微任务[then1],宏任务[timeou1, timeout2]
微任务: 执行 then1,输出 then1
微任务队列清空
当前任务队列:微任务[],宏任务[timeou1, timeout2]
宏任务: 输出 timeout1
输出 timeout2
当前任务队列:微任务[],宏任务[timeou2]
微任务: 为空跳过
当前任务队列:微任务[],宏任务[timeou2]
宏任务: 输出 timeout2
async/await 的执行
// async/await 写法
async function async1() {
console.log("async1 start");
await async2();
console.log("async1 end");
}
// Promise 写法
async function async1() {
console.log("async1 start");
Promise.resolve(async2()).then(() => console.log("async1 end"));
}
async function async1() {
console.log("async1 start");
await async2();
console.log("async1 end");
}
async function async2() {
console.log("async2");
}
async1();
setTimeout(() => {
console.log("timeout");
}, 0);
new Promise(function (resolve) {
console.log("promise1");
resolve();
}).then(function () {
console.log("promise2");
});
console.log("script end");
当前任务队列:宏任务:[ <script>
],微任务: []
宏任务: 输出: async1 start
遇到 async2,输出: async2
,并将 then(async1 end)加入微任务遇到 setTimeout,加入宏任务。 遇到 Promise,输出: promise1
,直接 resolve,将 then(promise2)加入微任务输出: script end
当前任务队列:微任务[promise2, async1 end],宏任务[timeout]
微任务: 输出: promise2
promise2 出队 输出: async1 end
async1 end 出队 微任务队列清空
当前任务队列:微任务[],宏任务[timeout]
宏任务: 输出: timeout
timeout 出队,宏任务清空
setTimerout 并不准确
const s = new Date().getSeconds();
console.log("script start");
new Promise((resolve) => {
console.log("promise");
resolve();
}).then(() => {
console.log("then1");
while (true) {
if (new Date().getSeconds() - s >= 4) {
console.log("while");
break;
}
}
});
setTimeout(() => {
console.log("timeout");
}, 2000);
console.log("script end");
四秒后输出:while、timeout
<!-- 以 Vue 为例 nextTick -->
总结
评论