图解同步以及异步
去年写了一个同步和异步之前的差异其中说了事件循环,但是有点模糊,这里再记载一下

从图中所知,js运行代码时会把任务放到main stack中,如图所示stack的特点是先进后出,后进先出
但是遇到异步代码会怎么样

如图所示,js引擎会优先执行同步代码,遇到异步代码会首先把异步代码通过web api挂载起来,他会把里面的代码加到一个队列中,队列的特性是先进先出后进后出,等异步代码执行完毕,再把里面的同步代码放入到main stack中运行
宏任务与微任务
当遇到异步任务时被加入到事件循环时,同时分为宏任务(macrotask)以及微任务(microtask)这两读音好像啊。
其中宏任务有
- script全部代码
- setTimeout
- setInterval
- setImmediate(浏览器暂时不支持,只有IE10支持,具体可见MDN)
- I/O操作
UI Rendering
微任务有
- Promise.then
- MutaionObserver
- process.nextTick (Node.js)
- Object.observe(废弃)
当js挂起异步任务的时候,其中有两个队列,一个是宏任务队列一个是微任务队列,其中是怎么运行的呢
- 选择当前要执行的任务队列,选择任务队列中最先进入的任务,如果任务队列为空即null,则执行跳转到微任务(MicroTask)的执行步骤。
- 将事件循环中的任务设置为已选择任务
- 执行任务
- 将事件循环中当前运行任务设置为null
- 将已经运行完成的任务从任务队列中删除
- microtasks步骤:进入microtask检查点
- 更新界面渲染
- 返回第一步
运行流程如下,一句话总结来说:微任务权重比宏任务要高在队列中优先执行
练习题
async function async1() {
console.log('async1 start');
await async2();
console.log('async1 end');
}
async function async2() {
console.log('async2');
}
console.log('script start');
setTimeout(function () {
console.log('setTimeout');
}, 0);
async1();
new Promise(function (resolve) {
console.log('promise1');
resolve();
}).then(function () {
console.log('promise2');
});
console.log('script end');
首先执行同步代码console.log('script start');
然后挂起一个宏任务 console.log('setTimeout');
再执行同步代码 console.log('async1 start');
继续执行async2的同步代码console.log('async2');
挂起微任务console.log('async1 end');
下面的new Promise执行同步代码console.log('promise1');
.then挂起微任务console.log('promise2');
执行同步代码console.log('script end');
所以执行结果是
script start
async1 start
async2
promise1
script end
async1 end
promise2
setTimeout
下面还有两题同理
setTimeout(() => {
console.log('A'); // 4
}, 0);
var obj = {
func: function () {
setTimeout(function () {
console.log('B'); //5
}, 0);
return new Promise(function (resolve) {
console.log('C'); // 1
resolve();
});
},
};
obj.func().then(function () {
console.log('D'); // 3
});
console.log('E'); // 2
let p = new Promise(resolve => {
resolve(1);
Promise.resolve().then(() => console.log(2));
console.log(4);
}).then(t => console.log(t));
console.log(3);