当前位置: 首页 > Web前端 > JavaScript

创建您自己的解决方案!setTimeout+Promise+Async输出顺序?简单的一个!!

时间:2023-03-26 21:39:52 JavaScript

前言大家好。我是林三鑫。关于EventLoop我有很多知识点。我通常会参加很多考试。事实上,它与我们的日常工作息息相关。知道EventLoop的执行顺序可以极大的帮助我们定位问题出在哪里。其实正常的EventLoop序列很好区分,但是setTimeout+Promise+async/await组合起来就很棘手了。今天就带大家闯过五关,杀六大将收服!!!注:本文不涉及Nodejs的执行机制同步&&异步什么是异步,什么是同步,我就不多说了,通过一个小故事来告诉大家。同步:你打电话到书店订书,老板让我查。如果你不挂机等,老板告诉你查到的结果,这期间你自己的事情也不能做。异步:你打电话给书店订书,老板说我查一下,等会儿告诉你,你挂了电话先做自己的事。JS执行机制其实并不难。JavaScript代码执行机制,我归结为三句话1.遇到同步代码直接执行2。遇到异步代码,先放在一边,保存它的回调函数。存放它的地方叫做事件队列。3.所有同步代码执行完毕后,按顺序从事件队列中取出所有存储的异步回调函数请看下面的执行例子console.log(1)//synchronoussetTimeout(()=>{console.log(2)//异步},2000);console.log(3)//同步setTimeout(()=>{console.log(4)//异步},0);console.log(5)//同步输出:13542macrotask&µtask如前所述,所有同步代码执行完毕后,从事件队列开始依次执行所有异步回调函数。其实eventqueue也是一个小团体,他们也有自己的规矩,就好像学校管理着很多社团,自己的社团也有自己的规矩。话虽如此,为什么事件队列需要有自己的规则呢?为什么不先想想为什么学校里的社团有自己的规矩和等级。就是因为有的人强,有的人弱,所以才有等级。其实事件队列也是一样的。事件队列是用来存放异步回调的,但是异步任务也是分类型的。异步任务分为宏任务和微任务,微任务的定时在宏任务之前执行。宏任务和微任务分别是什么?Macrotask#BrowserNodeI/O??setTimeout??setInterval??setImmediate??requestAnimationFrame??microtask#BrowserNodePromise.prototype.thencatchfinally??process.nextTick??MutationObserver??执行过程说一下整体的执行过程。例如,您可以按照我的问题解决步骤进行操作。基本上90%的问题都是无压力的!!!1.标记区分异步和同步2.异步中,标记区分macrotasks和microtasks3.轮数,一次一轮console.log(1)//synchronoussetTimeout(()=>{console.log(2)//异步:宏任务});console.log(3)//同步Promise.resolve().then(()=>{//异步:微任务console.log(4)})console.log(5)//第一轮同步指令:先输出同步执行output:1,3,5生成macrotasks:setTimeout,生成microtasks:Promise.prototype.then第二轮指令:executemicrotasksfirstOutput:4Generatedmacrotasks:None,Generatedmicrotasks:NoneRemainingmacrotasks:setTimeout,Remainingmicrotasks:None第三轮(结束)解释:ExecutedmacrotasksOutput:2Generatedmacrotasks:None,Generatedmicrotasks:NoneRemainingmacrotasks:none,remainingmicrotasks:none第一步是想idea解决我刚才提到的问题。你可以遵循这个想法。这个问题只是几分钟的事情。console.log(1)setTimeout(()=>{console.log(2)Promise.resolve().then(()=>{console.log(3)})});console.log(4)newPromise((resolve,reject)=>{console.log(5)}).then(()=>{console.log(6)setTimeout(()=>{console.log(7)})})console.log(8)第一步:mark注意:Promise的执行者是同步的!!!console.log(1)//同步setTimeout(()=>{console.log(2)//异步:宏任务setTimeout1Promise.resolve().then(()=>{//异步:微任务then1console.log(3)})});console.log(4)//同步newPromise((resolve,reject)=>{console.log(5)//同步resolve()}).then(()=>{//异步:microtaskthen2console.log(6)setTimeout(()=>{console.log(7)//asynchronous:macrotasksetTimeout2})})console.log(8)//synchronoussecondStep:轮数显示输出,剩下的先回合被执行。外层同步输出1,4,5,8宏任务:setTimeout1
微任务:then2宏任务:setTimeout1
微任务:then2第二轮微任务执行then26宏任务:setTimeout2
微任务:nomacrotask:setTimeout1,setTimeout2
microtask:no第三轮macrotask执行setTimeout12macrotask:no
microtask:nothen1macrotask:setTimeout2
microtask:then1执行第四轮microtaskthen13macrotask:no
microtask:nomacrotask:setTimeout2
microtask:no第五轮执行macrotasksetTimeout27宏任务:No
Microtask:No宏任务:No
Microtask:No第二轮level遇到Promise.then.then,如果有点迷糊,可以switch下面说说:then方法会自动返回一个新的Promise,也就是返回新的Promise,具体的Promise源码,大大家可以看看我的文章,手写Promise的原理,最通俗易懂的版本[阅读:1.1w,喜欢:430]setTimeout(()=>{console.log(1)},0)console.log(2)constp=newPromise((resolve)=>{console.log(3)resolve()}).then(()=>{console.log(4)}).then(()=>{console.log(5)})console.log(6)Step1:mark+conversion注意:这里的转换只是做题的时候,比较容易理解,平时不做这个转换,一般是不太好合适,falsesetTimeout(()=>{//异步:宏任务setTimeoutconsole.log(1)},0)console.log(2)//同步constp=newPromise((resolve)=>{//p是then1执行返回的新Promiseconsole.log(3)//同步resolve()}).then(()=>{//异步:microtaskthen1console.log(4)//持有prethenp.then(()=>{//异步:microtaskthen2console.log(5)})})console.log(6)//同步6Step2:轮数表示输出为生成剩余第一轮同步输出2,3,6macrotask:setTimeout
microtask:then1macrotask:setTimeout
microtask:then1第二轮microtask执行then14macrotask:none
microtask:then2macrotask:setTimeout
microtask:then2在第三轮执行microtaskthen25macrotask:none
microtask:nonemacrotask:setTimeout
microtask:nonefourth执行macrotasksetTimeout1Macrotask:None
Microtask:NoneMacrotask:无
微任务:无.then.then,如果你有点糊涂,你可以改一下。注意:then方法会自动返回一个新的Promise,即returnnewPromise。具体的Promise源码可以看我的文章。是的,手写的Promise原理,最通俗易懂的版本[阅读:1.1w,喜欢:430]newPromise((resolve,reject)=>{console.log(1)resolve()}).then(()=>{console.log(2)newPromise((resolve,reject)=>{console.log(3)resolve()}).then(()=>{console.log(4)}).then(()=>{console.log(5)})}).then(()=>{console.log(6)})第一步:mark+conversion注意:这里的转换只是针对题,比较容易理解,平时不做这个转换,平时做这个转换不合适,是错误的constp1=newPromise((resolve,reject)=>{//p1是then1执行console返回的新Promise.log(1)//同步resolve()}).then(()=>{//异步:微任务then1console.log(2)constp2=newPromise((resolve,reject)=>{//p2isthen2executionReturnednewPromiseconsole.log(3)//synchronousresolve()inthen1}).then(()=>{//异步:microtaskthen2console.log(4)//持有p2然后重新p2.then(()=>{//异步chronous:microtaskthen3console.log(5)})})//获取p1然后重启p1.then(()=>{//异步:microtaskthen4console.log(6)})})第二步:解释输出的轮数和生成剩余的第一轮执行外层同步输出1macrotask:none
microtask:then1macrotask:none
Microtask:then1在第二轮执行microtaskthen12,3个macrotasks:none
microtask:then2,then4macrotask:none
microtask:then2,then4在第三轮执行microtaskthen2round,then44,6macrotasks:none
microtasks:then3macrotasks:none
microtasks:then3执行第四轮microtasksthen35macrotasks:none
microtasks:nonemacrotasks:No
Micro-tasks:没有第四层,比上一层多了一个return,前面说了then方法会自动返回一个新的Promise,相当于returnnewPromise,但是如果你手动写returnPromise,return的就是承诺你手动编写newPromise((resolve,reject)=>{console.log(1)resolve()}).then(()=>{console.log(2)//再返回一个returnnewPromise((resolve,reject)=>{console.log(3)resolve()}).then(()=>{console.log(4)}).then(()=>{//等价于return,执行thisthen返回Promiseconsole.log(5)})}).then(()=>{console.log(6)})第一步:mark+转换由于returnthen3执行的是返回的Promise,所以then4其实就是then3Promise.then(),所以可以转换为then3.then4newPromise((resolve,reject)=>{console.log(1)//同步resolve()}).then(()=>{//异步:微任务then1cconsole.log(2)//then1中的同步resolve()newPromise((resolve,reject)=>{console.log(3)//then1中的同步resolve()}).then(()=>{//异步:microtaskthen2console.log(4)}).then(()=>{//异步:microtaskthen3console.log(5)}).then(()=>{//异步:microtaskthen4console.log(6)})})第二步:轮数,轮数,输出,剩余第一轮执行,外层同步输出1macrotask:nomicrotask:then1macrotask:no
Microtask:then1在第二轮执行microtaskthen12,3个macrotasks:None
Microtask:then2,then3,then4Macrotask:None
Microtask:then2,then3,then4第三轮执行microtasksthen2,then3,then44,5,6Macrotasks:None
Microtasks:None
Microtasks:None第五层newPromise((resolve,reject)=>{console.log(1)resolve()}).then(()=>{console.log(2)newPromise((resolve,reject)=>{console.log(3)resolve()}).then(()=>{console.log(4)}).then(()=>{console.log(5)})}).then(()=>{console.log(6)})newPromise((resolve,reject)=>{console.log(7)resolve()}).then(()=>{console.log(8)})第一步:mark+transformconstp1=newPromise((解决,拒绝)=>{//p1是then1执行返回的新Promiseconsole.log(1)//同步resolve()}).then(()=>{//异步:microtaskthen1console.log(2)constp2=newPromise((resolve,reject)=>{//p2是then2执行返回的新Promiseconsole.log(3)//then1中的同步resolve()}).then(()=>{//异步:microtaskthen2console.log(4)//获取p2并重新执行p2.then(()=>{//异步:microtaskthen3console.log(5)})})//获取p1并重新执行p1.then(()=>{//异步:微任务then4console.log(6)})})newPromise((resolve,reject)=>{console.log(7)//同步resolve()}).then(()=>{//异步:microtaskthen5console.log(8)})第2步:子轮轮数解释输出并生成其余部分。第一轮执行外同步输出1,7macrotasks:No
Microtasks:then1,then5Macrotasks:None
Microtasks:then1,then5第二轮执行microtasksthen1,then52,then5,8Macrotasks:None
Microtasks:then2,then4macrotask:none
microtask:then2,then4第三轮执行microtasksthen2,then44,6macrotask:none
microtask:then5macrotask:none
microtask:then5执行第四轮的micro-tasksthen55macro-tasks:no
micro-tasks:nomacro-tasks:no
micro-tasks:no第六层其实内部实现原理async/await依赖于Promise.prototype.then的连续嵌套,也在题中可以转换,如下所述感兴趣的朋友可以看看我的7张图,20分钟搞定的async/await原理!为什么这么久[阅读量:1.8w,点赞数:571]asyncfunctionasync1(){console.log(1);等待async2();console.log(2);}asyncfunctionasync2(){console.log(3);}console.log(4);setTimeout(function(){console.log(5);});async1()newPromise(function(resolve,reject){console.log(6);resolve();}).then(function(){console.log(7);});console.log(8);第一步:mark+conversion注意:这里的转换只是为了做题时的比较很容易理解,平时不要做这个转换,平时这样转换是不合适的console.log(4);//同步setTimeout(function(){console.log(5);//异步:宏任务setTimeout});//async1函数可以转换为console.log(1)//同步newPromise((resolve,reject)=>{console.log(3)//同步resolve()}).then(()=>{//异步:microtaskthen1console.log(2)})//async1函数结束newPromise(function(resolve,reject){console.log(6);//同步resolve();}).then(function(){//异步:microtaskthen2console.log(7);});console.log(8);//同步第二步:轮数表示剩余第一轮执行同步输出4,1,3,6,8宏任务:setTimeout
微任务:then1,then2宏任务:setTimeout
微任务:then1,then2在第二轮执行微任务taskthen1,then22,7macrotask:none
microtask:nomacrotask:setTimeout
microtask:no第三轮执行macrotasksetTimeout5macrotask:none
microtask:then5macroTask:None
微任务:没有课后作业最后给大家布置两个作业,帮助大家巩固本文所学的知识。也可以加入我的摸鱼群一起讨论解答加群点这里【进群】(https://juejin.cn/pin/6969565...),目前已有近千人加入学习。我会定期举办学习分享、模拟面试等学习活动,共同学习。共同进步!!!第一个问题(思考题)想想下面两个有什么区别?//第一种constp=newPromise((resolve,reject)=>{resolve()}).then(()=>console.log(1)).then(()=>console.log(2))//第二个constp=newPromise((resolve,reject)=>{resolve()})p.then(()=>console.log(1))p.then(()=>console.log(2))第二个问题(问题不大)asyncfunctionasync1(){console.log(1);等待async2();console.log(2);}asyncfunctionasync2(){console.log(3);}newPromise((resolve,reject)=>{setTimeout(()=>{resolve()console.log(4)},1000);}).then(()=>{console.log(5)newPromise((resolve,reject)=>{setTimeout(()=>{async1()resolve()console.log(6)},1000)}).then(()=>{console.log(7)}).then(()=>{console.log(8)})}).then(()=>{console.log(9)})newPromise((resolve,reject)=>{console.log(10)setTimeout(()=>{resolve()console.log(11)},3000);}).then(()=>{console.log(12)})第三题(有点难)这道题一分钟内搞定我接受奖。这道题需要有一定的Promise原理+async/await原理基础才能答得比较轻松。有兴趣的同学可以看看我之前写的文章。手写Promise的原理是最通俗易懂的。看懂版本[阅读:1.1w,喜欢:430]7张图,20分钟就能搞定的async/await原理!为什么花了这么长时间[阅读量:1.8w,喜欢:571]asyncfunctionasync1(){console.log('async1start')awaitasync2()console.log('async1end')}asyncfunctionasync2(){console.log('asyncstart')returnnewPromise((resolve,reject)=>{resolve()console.log('async2promise')})}console.log('scriptstart')setTimeout(()=>{console.log('setTimeout')},0);async1()newPromise((resolve)=>{console.log('promise1')resolve()}).then(()=>{console.log('promise2')}).then(()=>{console.log('promise3')})console.log('scriptend')结论如果您认为本文对您有帮助,请点赞,鼓励林三心哈哈哈或者可以加入我的摸鱼群,一起努力学习,我会定期模拟面试,复盘指导,答疑解惑,互相学习,共同进步!!