社区微信群开通啦,扫一扫抢先加入社区官方微信群
社区微信群
以下是一篇异步 (async/Promise/setTimeout) 面试题。看到网上不少解答,有的非常详细,同时也很啰嗦。有的分析得太简单,不免过于粗糙。本文试图以通俗易懂的语言来剖析一下这道面试题,还是不懂的话,来找我。在开始之前先介绍几个名词。
宏任务(macrotask):
宏任务指执行栈中待执行的任务(主线程上的同步任务)。
宏任务包括 script(整体代码)、setTimeout、setInterval、I/O、UI交互事件、postMessage、MessageChannel、setImmediate(Node.js 环境)。
宏任务与页面渲染的关系是:
第一次宏任务执行到结束 => 开始渲染页面 => 第二次宏任务执行到结束
微任务(microtask):
微任务指当前任务执行之后立即执行的任务。
微任务包括 Promise.then、MutaionObserver、process.nextTick(Node.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');
答案:
new Promise((resolved)=>{
console.log(1);
resolved();
}).then(()=>{
console.log(2);
})
Promise 函数执行顺序比较简单,Promise 内的任务为立即执行任务,也可以理解为同步任务。而 then 方法中的任务是微任务,会放在微任务列表中。所以以上执行顺序是1,2。
async 函数主要通过 await 关键字实现异步任务同步写法。如果没有 await 关键字,那么 async 函数跟普通函数没区别。
案例1:
async function async1() {
console.log('1');
await new Promise(function (resolve) {
setTimeout(function () {
resolve(888);
console.log('2');
},1000)
});
console.log('3');
}
async1()
以上函数如果没有 await 关键字,输出结果应该是1,3,2。加了 await 之后虽然有异步,但是也是按同步顺序执行,结果为1,2,3。
案例2:
async function async1() {
console.log(1);
await async2();
console.log(3);
}
async function async2() {
console.log(2);
}
async1();
new Promise(function (resolved) {
console.log(4);
})
按照案例1的理解,async1()函数执行之后输出顺序为1,2,3。后面有个 Promise 函数,函数内有立即执行代码 console.log(4)
。那么结果是不是 1,2,3,4?
async 是 promise 和 generator 的语法糖,所以 async1 方法等同于
async function async1() {
console.log(1);
new Promise((resolved)=>{
async2()
resolved();
}).then(()=>{
console.log(3);
})
}
async function async2() {
console.log(2);
}
这里可以看出 console.log(3)
在 Promise.then 中,是一个微任务。所以在立即执行任务 console.log(4)
之后执行。所以以上执行顺序为1,2,4,3。
setTimeout 函数是一个宏任务,需要等到 script 整体的宏任务执行完成之后再执行。
<script>
console.log(1);
setTimeout(function () {
console.log(2)
})
</script>
<script>
console.log(3)
</script>
打印结果为 1,3,2。第一个 script 是一个宏任务,setTimeout 是定时器类型的宏任务。而第二个 script 跟第一个是同一个列队。所以先执行 script 列队中的宏任务,再执行定时器宏任务。
根据以上的说明和案例,该面试题剖析如下:
题中有两个宏任务,第一个是 script 标签包含的整体脚本,第二个是 setTimeout 定时器宏任务。所以定时器中的任务 console.log('setTimeout')
肯定是最后执行。
async 中和 Promise 中的任务都是立即执行任务,即 console.log('async1 start')
和 console.log('promise1')
,可以理解为是同步任务。await是一个让出线程的标志。await后面的表达式也会立即执行,即 console.log('async2')
也会同步执行。而 await 后面的代码会加入到微任务列队中。此时微任务列队中有 console.log('async1 end')
。
在Promise.then() 函数中有微任务 console.log('promise2')
。
整理一下以上面试题。
(1)同步或者立即执行任务有:
console.log(‘script start’);
console.log(‘async1 start’);
console.log(‘async2’);
console.log(‘promise1’);
console.log(‘script end’);
(2)微任务有:
console.log(‘async1 end’);
console.log(‘promise2’);
(3)除了第一个script 宏任务,第二个宏任务是:
console.log(‘setTimeout’);
所以执行顺序为:
如果觉得我的文章对您有用,请随意打赏。你的支持将鼓励我继续创作!