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')
关于上述这道题除了要考虑同步异步的问题,还涉及到js中的微任务和宏任务,就这道题我说一下关于我的理解。
同步和异步任务分别进入不同的执行”场所”,同步的任务进入主线程,异步的进入Event Table并注册函数。当异步函数完成时,Event Table会将这个函数移入Event Queue。主线程内的任务执行完毕为空,会去Event Queue读取对应的函数,进入主线程执行。上述过程会不断重复,也就是常说的Event Loop(事件循环)。
除此之外,在js中还存在宏任务和微任务。
js中的宏任务一般是:包括整体代码script,setTimeout,setInterval。
微任务是:Promise,process.nextTick。
说一下我对于async/await的理解:
1.async做了一件什么事情?
async将你的函数返回值转换为promise对象,不需要显式地返回promise对象,async关键字自动将函数的返回值变为promise对象。
2.await的作用
await关键字只能在带有async关键字的函数内部使用,在外部使用时会报错。await等待的是右侧的[表达式结果],如果右侧是一个函数,等待的是右侧函数的返回值,如果右侧的表达式不是函数则直接是右侧的表达式。await在等待时会让出线程阻塞后面的执行。await的执行顺序为从右到左,会阻塞后面的代码执行,但并不是直接阻塞await的表达式。
await之后如果不是promise,await会阻塞后面的代码,会先执行async外面的同步代码,等外面的同步代码执行完成在执行async中的代码。
如果它等到的是一个 promise 对象,await 也会暂停async后面的代码,先执行async外面的同步代码,等着 Promise 对象 fulfilled,然后把 resolve 的参数作为 await 表达式的运算结果。
一段代码执行时,会先执行宏任务中的同步代码:
-
如果执行中遇到
setTimeout
之类宏任务,那么就把这个
setTimeout
内部的函数推入「宏任务的队列」中,下一轮宏任务执行时调用。 -
如果执行中遇到
promise.then()
之类的微任务,就会推入到「当前宏任务的微任务队列」中,在本轮宏任务的同步代码执行都完成后,依次执行所有的微任务1、2、3。
上述这道题的执行顺序为:
第一个宏任务:
console.log('script start')
第一个宏任务中的第一个微任务:
async1();
async function async1() {
console.log('async1 start')
await async2()
console.log('async1 end')
}
async function async2() {
console.log('async2')
}
在第一个微任务中存在await关键字,因此先输出
async1 start
接着输出
async2
await阻塞后面的代码执行,因此跳出async函数执行下一个微任务
第一个宏任务中的第二个微任务:
new Promise(function( resolve ) {
console.log('promise1')
resolve();
} ).then(function() {
console.log('promise2')
} )
先输出:
promise1
碰到promise.then这个微任务会先执行本轮宏任务的同步代码再执行微任务
接着输出:
script end
再挨个执行所有的微任务,依次输出:
promise2
async1 end
第一个宏任务执行完成,执行第二个宏任务:
setTimeout(function() {
console.log('setTimeout')
}, 0)
输出结果为:
setTimeout
这就是这道题的解题思路。