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

诺言懂,读再读!

时间:2023-03-28 17:59:44 HTML

Promise哪些API涉及微任务?在Promise中,只有状态改变后需要执行的回调才被认为是微任务,例如then、catch、finally,其他所有代码执行都是宏任务(同步执行)。上图中,蓝色是同步执行,黄色是异步执行(丢进microtask队列)。这些微任务什么时候添加到微任务队列中?我们按照ecma规范来看这个问题:如果此时Promise状态为pending,成功或失败的回调会分别添加到[[PromiseFulfillReactions]]和[[PromiseRejectReactions]]中。如果你看手写的Promise代码,你应该能发现有两个数组存储了这些回调函数。如果此时Promise状态不是pending,回调就会变成PromiseJobs,即microtasks。同样的then,不同的微任务执行主Promise.resolve().then(()=>{console.log("then1");Promise.resolve().then(()=>{console.log("then1-1");});}).then(()=>{console.log("then2");});虽然then是同步执行的,而且状态发生了变化。但是这并不是说我们每次遇到then都需要把它的callback丢到microtask队列中,而是等待then的callback执行完毕,再根据情况进行相应的操作。基于此,我们可以得出第一个结论:在链式调用中,只有前一个then的回调执行完后,后一个then的回调才会被加入到微任务队列中。中级的都知道,Promiseresolve之后,后面then中的回调会立马进入microtask队列。那么您认为以下代码的输出是什么?让p=Promise.resolve();p.then(()=>{console.log("then1");Promise.resolve().then(()=>{console.log("then1-1");});}).then(()=>{console.log("then1-2");});p.then(()=>{console.log("then2");});按照最初的认知,不难得出then2会在then1-1之后输出,但实际情况恰恰相反。基于此,我们得出第二个结论:每次链式调用的开始都会先依次进入microtask队列。接下来我们换个写法:letp=Promise.resolve().then(()=>{console.log("then1");Promise.resolve().then(()=>{console.log("then1-1");});}).then(()=>{console.log("then2");});p.then(()=>{console.log("then3");});上面的代码其实有一个陷阱,then每次都会返回一个新的Promise,此时p不是Promise.resolve()生成的,而是上一个then生成的,所以then3应该打印在then2之后。顺便说一句,我们也可以优化之前的结论:同一个Promise的每次链式调用的开始都会先依次进入microtask队列。什么时候打印advancedthen1-2?Promise.resolve().then(()=>{console.log("then1");Promise.resolve().then(()=>{console.log("then1-1");返回1;}).then(()=>{console.log("then1-2");});}).then(()=>{console.log("then2");}).then(()=>{console.log("then3");}).then(()=>{console.log("then4");});分析:第一次resolve后,firstthen的回调进入microtask队列并执行,打印then1的第二次resolve后,内部的firstthencallback进入microtask队列。此时外部的firstthen回调全部执行完毕,需要将外部的secondthen回调插入到微任务队列中。执行microtasks,打印then1-1和then2,然后将then中的回调插入到microtask队列中执行microtasks,打印then1-2和then3,后面的内容就不一一解释了。接下来,我们将return1修改一下,结果会大不相同:Promise.resolve().then(()=>{console.log("then1");Promise.resolve().then(()=>{console.log("then1-1");returnPromise.resolve();}).then(()=>{console.log("then1-2");});}).then(()=>{console.log("then2");}).then(()=>{console.log("then3");}).then(()=>{console.log("then4");});当我们返回Promise.resolve()时,猜猜then1-2什么时候会被打印出来?答案是打印最后一个。为什么then返回不同的东西,microtasks的执行顺序变化这么大?以下是笔者的分析。PS:然后返回一个新的Promise,会用这个Promise解析返回值。首先需要理解这个概念。根据PromiseA+规范,根据规范2.3.2,如果一个Promise被resolved,则需要在其中添加一个then并resolved。if(xinstanceofMyPromise){if(x.currentState===PENDING){}else{x.then(resolve,reject);}return;}以上代码摘自手写的Promise实现。按照A+的规范,如果我们在then中返回Promise.resolve,就会多入队一个microtask,但是这个结论还是与实际不符,所以需要找其他权威文档。根据ECMA-262规范,根据规范25.6.1.3.2,当一个Promise被resolved时,会产生一个NewPromiseResolveThenableJob,它是PromiseJobs的一种,即microtasks。此作业使用提供的thenable及其then方法来解决给定的承诺。此过程必须作为作业进行,以确保then方法的评估发生在任何周围代码的评估完成之后。并且Jobs会调用一次then函数来resolvePromise,这也会生成另一个微任务。这就是微任务被触发两次的原因。