js执行顺序
本篇随笔主要通过两个案例来梳理promise执行顺序和promise、async/await执行顺序,介绍之前默认大家已经了解这两个api的使用方法并且了解js宏任务与微任务概念。文章末尾会附上相关资料,有兴趣的可以查看。
一、promise
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 start、script end、promise1、promise2、setTimeout;
开始分析:
1、第一个输出大家都没问题,代码执行到第二行遇到setTimeout,js引擎把定时器回调函数当做一个宏任务放入回调队列(callback queue),让出js主线程继续往下执行;
2、到第五行是个promise.resolve()异步执行函数(微任务),所以js引擎把then后面的回调函数放入当前调用栈末尾,让出js主线程继续往下执行。
3、执行完最后一行,输出script end;此时at the end of a task,we process microtasks(当前宏任务执行完毕,开始执行微任务,也就是第2步中的),输出promise1,接着继续往下执行,又遇到了promise回调函数,所以也把第二个then后面的回调函数当成微任务放入当前调用栈末尾。
4、因为当前调用栈中的微任务必须先于下一个宏任务执行,所以继续执行第3步中的微任务,输出promise2。
5、当前调用栈中代码全部执行完,根据事件循环机制,js引擎将回调队列中宏任务放入调用栈中执行,输出setTimeout。
二、promise、async/await
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
async start
async2
promise1
script end
promise2
async1 end
setTimeout
分析:
1、首先输出script start,然后将setTimeout的回调函数当成一个宏任务放入回调队列,让出js主线程,执行async1()。虽然async1是用async声明的异步函数,但是他本身依旧是一个立即执行函数,所以进入async1,执行输出async start。
2、直到遇到了await(async wait的缩写)这个熊孩子,看js是怎么处理他的。遇到await之后,js会执行完他后面的表达式/函数,然后跳出这个异步函数。这里呢,await后面跟的用async声明的async2异步函数,所以执行完,await后面是一个promise对象(未resolve),跳出async1。
3、执行到new promise,立即输出promise1,然后是他的resolve(),所以暂停执行,把promise的回调函数当成一个微任务放到当前调用栈末尾,跳出promise,执行最后一行,输出script end。
4、注意注意,下面的不好理解。接着执行第2步中的async1,此时await后面的promise开始resolve,所以js将async2函数的返回值(promise对象)的回调函数当做一个微任务,放到当前调用栈末尾,js主线程又让出async1函数。
5、此时当前调用栈中宏任务执行完毕,立即按顺序执行微任务。也就是第3步中的,直接输出promise2。接着执行第4步中产生的微任务(当然,没有执行任何东西),这会之后,js才正式通过await那一行,输出async1 end。这就是为什么promie2先于async1 end 输出。
6、扫扫尾,js执行下一个宏任务,输出setTimeout,到此全部结束。
上面的我加粗的那两个输出内容,是大家最疑惑的地方,也是最不好理解的地方,希望大家可以好好解读一下我的分析。
手敲不易,如果对你来说有一点帮助,请献出你们的免费推荐。下面是一些我参考的资料和一个学习资料。
Tasks, microtasks, queues and schedules
Tasks, microtasks, queues and schedules(翻译)
学习资料