这篇文章的参考主要来自:javascript忍者秘籍(第二版)13章
事件循环图:
宏任务:从浏览器角度,代表一个离散的,独立的工作单元
比如:创建主文档对象,解析HTML,执行主线(全局)js代码
更改当前URL和各种事件(页面加载,输入,网络事件,定时器)
微任务:是更小的任务,微任务要尽可能快的,通过异步方式执行
比如:DOM变化(MutationObserver),Promise回调函数
由于因为async await 本身就是promise+generator的语法糖,所以await后面的也是
下面来看一道经典的关于事件循环的面试题:
setTimeout(function () {
console.log(1);
});
new Promise(function(resolve,reject){
console.log(2)
resolve(3)
}).then(function(val){
console.log(val);
})
console.log(4);
上面的代码执行结果为:2,4,3,1
我们结合上面的事件循环图来理解下输出结果
1.首先执行主线(全局js代码),碰到定时器代码,定时器时间为0表示尽快执行
定时器是宏任务,于是把定时器任务放入宏任务队列
现在宏任务队列中是(主线,定时器)
2.接着往下解析,开始执行promise的代码,遇到promise里的2直接输出
3.promise中有一个异步方法,是微任务,于是把他放到微任务队列
4.继续往下执行全局代码,输出4
5.跑完上面的流程,js主线(全局)代码执行完成,同时也是第一个宏任务执行完成了
6.按照事件循环图的流程,开始检查微任务队列是否有任务,有的话执行所有微任务,输出3
7.第一轮循环结束,更新UI主线(全局)任务从宏任务中出队
8.开始新的一轮事件循环,执行队首的宏任务即之前的定时器任务输出1
通过上面的例子我们也可以知道,定时器的执行时间机制
因为js单线程的本质,我们只能控制定时器何时被加入宏任务队列,而无法控制其何时执行
再看一个例子:
//请写出输出内容
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');
/*
script start
async1 start
async2
promise1
script end
async1 end
promise2
setTimeout
*/
上面这个例题可能会有一些地方难理解,主要是async和await
实际上可以把他们等价成promise的写法就好理解了
async function async1() {
console.log('async1 start');
await async2();
console.log('async1 end');
}
等价于
async function async1() {
console.log('async1 start');
Promise.resolve(async2()).then(() => {
console.log('async1 end');
})
}
nodejs中的事件循环不同于浏览器,下次总结了加在本文后