前言为什么要写这篇文章?因为这道题涉及到很多知识点:同步任务、异步任务、宏任务、微任务、任务队列、执行栈、js运行机制、EventLoop,所以想整理一下写一篇文章,希望对小伙伴们有所帮助帮助!经典问题中promise、async、await的如下代码,console打印的顺序是怎样的?异步函数async1(){console.log('async1start');等待async2();console.log('async1end');}asyncfunctionasync2(){console.log('async2');}console.log('脚本开始');setTimeout(function(){console.log('setTimeout');},0)async1();newPromise(function(resolve){console.log('promise1');resolve();}).then(function(){console.log('promise2');});console.log('脚本结束');复制代码正确答案脚本startasync1startasync2promise1scriptendasync1endpromise2setTimeout复制代码注意:因为是前端面试题,所以答案是基于浏览器的eventloop机制。在node平台上运行时会有差异。相信大部分前端都知道,这道题是关于js中的事件循环和任务队列知识点同步任务,异步任务,宏任务,微任务,任务队列,执行栈,js运行机制,EventLoop考察事件js中的循环和任务队列。请注意以下几点:Promise优先于setTimeout宏任务。因此,setTimeout回调将在最后执行。一旦定义了Promise,就会立即执行Promise的rejection和resolve,是异步执行的回调。所以resolve()会被放到callback队列中,main函数执行完,setTimeout执行前调用await后,才会放出线程。标有async的函数将返回一个Promise对象。async引入了async函数声明来定义返回AsyncFunction对象的异步函数。当调用异步函数时,它会返回一个Promise对象。当异步函数返回一个值时,Promise的resolve方法将负责。传递这个值当异步函数抛出异常时,Promise的reject方法也会传递这个异常值。它返回一个Promise对象来返回结果。最大的特点就是会通过async/await进行异步操作,但是写法和结构和我们平时写的(同步代码)是一样的。await运算符用于等待Promise对象。await表达式会暂停当前异步函数的执行,等待Promise处理完毕。如果Promise处理正常,回调的resolve函数参数作为await表达式值,继续执行async函数await表示放弃线程操作返回值(return_value):返回Promise对象的处理结果。如果等待的不是Promise对象,则返回值本身。因此,当await操作符后面的表达式为Promise时,其返回值实际上是Promise回调函数resolve的参数。我们都知道Promise是一个Promise函数立即执行,但是他的成功(或失败:reject)回调函数resolve是一个异步执行回调。resolve()执行时,任务会被放入回调队列,等调用栈空闲时,事件循环会把它拿走。promiseasync将返回Promise对象。如果返回值不是Promise对象,则调用Promiseresolve替换它。Promise对象async/await建立在Promise机制之上,总结一句话:带async关键字的函数,它使得你函数的返回值必须是一个promise对象。EvenLoop这里简单介绍一下EventLoop的概念。最近这段时间我会写一篇关于EventLoop的文章Javascript是单线程的,所有的同步任务都会在主线程中执行除了主线程之外,还有一个任务队列。每当异步任务有结果时,都会将一个事件插入到任务队列中。当主线程中的任务全部执行完毕后,系统会“依次”读取任务队列中的事件。对应的异步任务进入主线程开始执行异步任务,会有区别,所以它们的执行优先级也会不同。大致分为微任务(microtask,如:Promise、MutaionObserver等)和宏任务(macrotask,如:setTimeout、setInterval、I/O等)。在同一个事件循环中,微任务总是在宏任务之前执行。主线程会重复以上步骤,直到所有任务执行完毕。标题结果分析过程的第一步是输出。虽然输出的scriptstart里面有两个函数声明,里面有async关键字,但是没有调用我们就不看,直接打印同步代码console.log('scriptstart')。第二步输出async1start。当async1函数执行时,async表达式定义的函数也会立即执行。前面我们说过当你看到带有async关键字的函数时不要惊慌,它只是将返回值包装成一个promise,所以很常见的打印console.log('async1start')第三步输出async2async2是async定义的函数,输出async2并返回promise对象,await后中断async函数,先执行async外面的同步代码,现在直接打印console.log('async2')第4步,输出promise1并执行newPromise(),Promiseconstructor是同步代码,直接调用,所以打印console.log('promise1')。第五步输出scriptend,因为上一步先打印了promise1,然后执行到resolve的时候,跳出promise继续往下执行,所以就是Printconsole.log('scriptend')第六步输出async1end因为await定义的promise已经执行完毕并返回结果,所以继续执行async1函数之后的任务,即console.log('async1end')第七步是输出promise2是因为之前的newpromise被放到了resolve回调中,而这个resolve又被放到了调用栈中执行,所以打印了console.log('promise')。第八步,输出setTimerout,最后执行定时器(宏任务)setTimeout,打印console.log('setTimerout')主题变体1在这个变体中,我也将async2中的函数改成了Promise函数,代码如下如下:asyncfunctionasync1(){console.log('async1start');等待async2();控制台.log('async1end');}asyncfunctionasync2(){//async2进行以下更改:newPromise(function(resolve){console.log('promise1');resolve();}).then(function(){console.log('promise2');});}console.log('scriptstart');setTimeout(function(){console.log('setTimeout');},0)async1();newPromise(function(resolve){console.log('promise3');resolve();}).then(function(){console.log('promise4');});console.log('脚本结束');复制代码正确答案脚本startasync1startpromise1promise3scriptendpromise2async1endpromise4setTimeout复制代码titlevariant2我把async1中await之后的代码和async2的代码改成异步的,代码如下:asyncfunctionasync1(){console.log('async1start');等待async2();//修改如下:setTimeout(function(){console.log('setTimeout1')},0)}asyncfunctionasync2(){//修改如下:setTimeout(function(){console.log('setTimeout2')},0)}console.log('脚本开始');setTimeout(function(){console.log('setTimeout3');},0)async1();newPromise(function(resolve){console.log('promise1');resolve();}).then(function(){console.log('promise2');});console.log('脚本结束');代码正确答案scriptstartasync1startpromise1scriptendpromise2setTimeout3setTimeout2setTimeout1复制代码TitleVariation3本题整体与上题类似,代码如下:asyncfunctiona1(){console.log('a1start')awaita2()console.log('a1end')}asyncfunctiona2(){console.log('a2')}console.log('scriptstart')setTimeout(()=>{console.log('setTimeout')},0)Promise.resolve().then(()=>{console.log('promise1')})a1()letpromise2=newPromise((resolve)=>{resolve('promise2.then')控制台。log('promise2')})promise2.then((res)=>{console.log(res)Promise.resolve().then(()=>{console.log('promise3')})})控制台.log('scriptend')复制代码正确答案scriptstarta1starta2promise2scriptendpromise1a1endpromise2.thenpromise3setTimeout复制代码最后,如果本文对您有帮助,请为本文点个赞??????欢迎大家加入,一起学习前端,共同进步!
