为何写这篇文章?前端
由于这道题涉及了不少知识点:同步任务、异步任务、宏任务、微任务、任务队列、执行栈、js运行机制、EventLoop,因此想整理一下,写一篇文章,但愿对小伙伴们有所帮助!node
下面这段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'); 复制代码
正确答案promise
script start async1 start async2 promise1 script end async1 end promise2 setTimeout 复制代码
注:由于是一道前端面试题,因此答案是以浏览器的eventloop机制为准的,在node平台上运行会有差别浏览器
相信大多数前端都知道,这道题考的就是js里面的事件循环和任务队列markdown
同步任务、异步任务、宏任务、微任务、任务队列、执行栈、js运行机制、Event Loop异步
这题考察的是js中的事件循环和任务队列,注意如下几点:async
它经过返回一个 Promise 对象来返回结果最大的特色是:函数
经过 async / await 将异步的操做,但写法和结构倒是和咱们平时写的(同步代码)是同样
返回值(return_value):返回 Promise 对象的处理结果。若是等待的不是 Promise 对象,则返回该值自己,oop
因此,当await操做符后面的表达式是一个Promise的时候,它的返回值,实际上就是Promise的回调函数resolve的参数
咱们都知道Promise是一个当即执行函数,可是他的成功(或失败:reject)的回调函数resolve倒是一个异步执行的回调。当执行到resolve()时,这个任务会被放入到回调队列中,等待调用栈有空闲时事件循环再来取走它
async会返回Promise对象,若是返回值不是Promise对象则调用Promise resolve来换成Promise对象
async/await创建在Prmise机制上
总结一句话:带async关键字的函数,它使得你的函数的返回值一定是 promise 对象上
这里先简单的说一些Event Loop的概念,在最近这段时间我会写一篇关于Event Loop的文章
Javascript是单线程的,全部的同步任务都会在主线程中执行
主线程以外,还有一个任务队列。每当一个异步任务有结果了,就往任务队列里塞一个事件。 当主线程中的任务,都执行完以后,系统会 “依次” 读取任务队列里的事件。与之相对应的异步任务进入主线程,开始执行
异步任务之间,会存在差别,因此它们执行的优先级也会有区别。大体分为 微任务(micro task,如:Promise、MutaionObserver等)和宏任务(macro task,如:setTimeout、setInterval、I/O等)。同一次事件循环中,微任务永远在宏任务以前执行
主线程会不断重复上面的步骤,直到执行完全部任务
第一步,输出script start
虽然有两个函数声明,有async关键字,可是没有调用咱们就不看,直接打印同步代码console.log(‘script start’)
第二步,输出async1 start
在执行async1这个函数的时候,async表达式定义的函数也是当即执行 在前面咱们说过看到带有async关键字函数,不用慌,它仅仅是把return值包装成了promise,因此就很普通的打印 console.log( 'async1 start' )
第三步,输出async2
async2是async定义的函数,输出async2并返回promise对象, await后,中断async函数,先执行async外的同步代码, 目前就直接打印 console.log('async2')
第四步,输出promise1
执行new Promise(),Promise构造函数是直接调用的同步代码,因此就打印console.log( 'promise1' )
第五步,输出script end
由于上一步先打印了promise1,而后执行到resolve的时候,而后跳出promise继续向下执行,因此就打印console.log( 'script end' )
第六步,输出async1 end
由于await定义的这个promise已经执行完,而且返回结果,因此继续执行async1函数后的任务,就是console.log(‘async1 end’)
第七步,输出promise2
由于前面的new promise放进resolve回调,这个resolve被放到调用栈执行,因此就打印console.log('promise')
第八步,输出setTimerout
最后执行定时器(宏任务)setTimeout,打印console.log( 'setTimerout' )
在这个变式中我将async2中的函数也变成了Promise函数,代码以下:
async function async1() { console.log('async1 start'); await async2(); console.log('async1 end'); } async function async2() { //async2作出以下更改: new Promise(function(resolve) { console.log('promise1'); resolve(); }).then(function() { console.log('promise2'); }); } console.log('script start'); setTimeout(function() { console.log('setTimeout'); }, 0) async1(); new Promise(function(resolve) { console.log('promise3'); resolve(); }).then(function() { console.log('promise4'); }); console.log('script end'); 复制代码
正确答案
script start async1 start promise1 promise3 script end promise2 async1 end promise4 setTimeout 复制代码
我将async1中await后面的代码和async2的代码都改成异步的,代码以下:
async function async1() { console.log('async1 start'); await async2(); //更改以下: setTimeout(function() { console.log('setTimeout1') },0) } async function async2() { //更改以下: setTimeout(function() { console.log('setTimeout2') },0) } console.log('script start'); setTimeout(function() { console.log('setTimeout3'); }, 0) async1(); new Promise(function(resolve) { console.log('promise1'); resolve(); }).then(function() { console.log('promise2'); }); console.log('script end'); 复制代码
正确答案
script start async1 start promise1 script end promise2 setTimeout3 setTimeout2 setTimeout1 复制代码
这道题总体来讲与上面题大同小异与,代码以下:
async function a1 () { console.log('a1 start') await a2() console.log('a1 end') } async function a2 () { console.log('a2') } console.log('script start') setTimeout(() => { console.log('setTimeout') }, 0) Promise.resolve().then(() => { console.log('promise1') }) a1() let promise2 = new Promise((resolve) => { resolve('promise2.then') console.log('promise2') }) promise2.then((res) => { console.log(res) Promise.resolve().then(() => { console.log('promise3') }) }) console.log('script end') 复制代码
正确答案
script start a1 start a2 promise2 script end promise1 a1 end promise2.then promise3 setTimeout 复制代码
若是以为本文还不错,记得点个赞哦!
欢迎你们加入,一块儿学习前端,共同进步!