Promise、宏任务、微任务相关易错点总结这部分是对Promise输出结果相关易错点的理解和分析。async/await相关输出asyncfunctionf1(){console.log(1)awaitf2()console.log(3)}asyncfunctionf2(){console.log(2)}Promise.resolve().then(()=>{console.log(4)})f1()console.log('start')//最后的输出是:12start43如果没有async/await,只有Promise,就好多了这里更容易理解。因此这里的易错点分为两部分:fn函数执行async/await时如何将async/await转义成Promise表达式newPromise(fn)escapewishfunctionasyncfunctionf1(){console.log(1)awaitf2()console.log(3)}asyncfunctionf2(){console.log(2)}转义函数f1(){returnnewPromise((resolve)=>{console.log(1)constp=f2()p.then(()=>{console.log(3)resolve()})})}functionf2(){returnnewPromise((resolve)=>{console.log(2)resolve()})}f1()//123转义代码,执行f1时,跟随Promise链接,很容易判断输出结果是在123newPromise(fn)中的fn函数执行之前的时候项目中,有一个自定义的Promise(参考地址)是按照PromiseA+协议实现的,主要看这一段:classMyPromise{status=statusEnum.PENDING;resArr=[];rejArr=[];值=空;原因=空;构造函数(fn){constresolve=(val)=>{//...};constreject=(val)=>{//...};尝试{fn(解决,拒绝);}抓住(错误){拒绝(错误);}}then(onResolve,onReject){//...}catch(onReject){//...}}如你所见,当newPromise(fn)时,fn在构造函数中立即执行,即可以看做是同步代码再看前面的例子,结合转义结果,输出更容易理解asyncfunctionf1(){console.log(1)//立即在f1console返回的Promise对象的construstor中执行awaitf2().log(3)//插入微任务}asyncfunctionf2(){console.log(2)//在f2返回的Promise对象的构造函数中立即执行}Promise.resolve().then(()=>{//Plug进入微任务console.log(4)})f1()//先输出立即执行的结果12console.log('start')//输出立即执行的结果start//执行微任务阶段,此时microtaskqueueFor[()=>{console.log(4)},()=>{console.log(3)}]//执行microtaskqueue,输出4个3个macrotasks和js线程时间切片Promise.resolve().then(()=>{setTimeout(()=>{console.log(2)})})setTimeout(()=>{Promise.resolve().then(()=>{console.log(1)})})//输出12分析上面的代码,先看第一个执行的内容macrotaskphase//假设有一个pushToNextMacroTask方法,将函数push到下一个macrotask执行//第一个macrotaskphaseconstmicroTask=[]microTask.push(()=>{setTimeout(()=>{console.log(2)})})pushToNextMacroTask(()=>{Promise.resolve().then(()=>{console.log(1)})},0)//执行第一个的微任务阶段macrotaskmicroTask.forEach(func=>func())/***此时执行以下内容*pushToNextMacroTask(()=>{console.log(2)},0)*/这里有个分歧点。在第一个macrotask阶段执行了两次pushToNextMacroTask将方法push到下一个macrotask,那么下一个macrotask是同一个macrotask吗?对这部分代码进行Performance快照后,结果如下:可以发现,虽然两个setTimeouts的时序都是0,但是tasks并不会放在同一个macrotask中最终解析结果如下:Promise.resolve().then(()=>{setTimeout(()=>{console.log(2)})})setTimeout(()=>{Promise.resolve().then(()=>{console.log(1)})})/***假设宏任务列表:macroTaskList=[]*假设微任务列表:microTaskList=[]*1.第一个宏任务阶段*执行Promise。resolve().then,将其回调放入微任务队列*microTaskList=[*()=>{*setTimeout(()=>{*console.log(2)*})*}*]*执行setTimeout,Put其回调函数进入下一个macrotask*macroTaskList=[*()=>{console.log(1)}*]*1.1第一个macrotask的Microtask阶段*constmicroTask=macroTaskList.shift()*microTask()*ExecutesetTimeout并将其回调插入到下一个宏任务*macroTaskList=[*()=>{console.log(1)},*()=>{console.log(2)}*]*2.第二个宏任务阶段*constmacroTask=macroTaskList/shift()*macroTask()//输出1*2.2第二个macrotask的microtask阶段,没有microtask*3.第三个macrotask阶段*constmacroTask=macroTaskList/shift()*macroTask()//Output2**总结:*Output12*/js线程和渲染线程互斥,浏览器分配一个时间片给js线程执行,包括多个宏任务。连续执行两次setTimeout(fn,0),这两个fn会被放到两个macrotask中,不是同一个macrotask。
