需要真正明白的Promise
相关学习推荐:javascript学习教程
Promise 关于 API 这块大家应该都能熟练使用,但是和微任务相关的你可能还存在知识盲区。
前置知识
在开始正文前,我们先把本文涉及到的一些内容提前定个基调。
Promise 哪些 API 涉及了微任务?
Promise 中只有涉及到状态变更后才需要被执行的回调才算是微任务,比如说 then
、 catch
、finally
,其他所有的代码执行都是宏任务(同步执行)。

上图中蓝色为同步执行,黄色为异步执行(丢到微任务队列中)。
这些微任务何时被加入微任务队列?
这个问题我们根据 ecma 规范来看:
如果此时 Promise 状态为 pending,那么成功或失败的回调会分别被加入至
[[PromiseFulfillReactions]]
和[[PromiseRejectReactions]]
中。如果你看过手写 Promise 的代码的话,应该能发现有两个数组存储这些回调函数。如果此时 Promise 状态为非 pending 时,回调会成为 Promise Jobs,也就是微任务。
了解完以上知识后,正片开始。
同一个 then,不同的微任务执行
初级
Promise.resolve() .then(() => { console.log("then1"); Promise.resolve().then(() => { console.log("then1-1"); }); }) .then(() => { console.log("then2"); });复制代码
以上代码大家应该都能得出正确的答案:then1 → then1-1 → then2
。
虽然 then
是同步执行,并且状态也已经变更。但这并不代表每次遇到 then
时我们都需要把它的回调丢入微任务队列中,而是等待 then
的回调执行完毕后再根据情况执行对应操作。
基于此,我们可以得出第一个结论:链式调用中,只有前一个 then
的回调执行完毕后,跟着的 then
中的回调才会被加入至微任务队列。
中级
大家都知道了 Promise resolve
后,跟着的 then
中的回调会马上进入微任务队列。
那么以下代码你认为的输出会是什么?
let 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
后输出,但是实际情况却是相反的。
基于此我们得出第二个结论:每个链式调用的开端会首先依次进入微任务队列。
接下来我们换个写法:
let p = 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 的每个链式调用的开端会首先依次进入微任务队列。
高级
以下大家可以猜猜 then1-2
会在何时打印出来?
Promise.resolve() .then(() => { console.log("then1"); Promise.resolve() .then(() => { console.log("then1-1"); return 1; }) .then(() => { console.log("then1-2"); }); }) .then(() => { console.log("then2"); }) .then(() => { console.log("then3"); }) .then(() => { console.log("then4"); });复制代码
这题肯定是简单的,记住第一个结论就能得出答案,以下是解析:
第一次
resolve
后第一个then
的回调进入微任务队列并执行,打印then1
第二次
resolve
后内部第一个then
的回调进入微任务队列,此时外部第一个then
的回调全部执行完毕,需要将外部的第二个then
回调也插入微任务队列。执行微任务,打印
then1-1
和then2
,然后分别再将之后then
中的回调插入微任务队列执行微任务,打印
then1-2
和then3
,之后的内容就不一一说明了
接下来我们把 return 1
修改一下,结果可就大不相同啦:
Promise.resolve() .then(() => { console.log("then1"); Promise.resolve() .then(() => { console.log("then1-1"); return Promise.resolve(); }) .then(() => { console.log("then1-2"); }); }) .then(() => { console.log("then2"); }) .then(() => { console.log("then3"); }) .then(() => { console.log("then4"); });复制代码
当我们 return Promise.resolve()
时,你猜猜 then1-2
会何时打印了?
答案是最后一个才被打印出来。
为什么在 then
中分别 return
不同的东西,微任务的执行顺序竟有如此大的变化?以下是笔者的解析。
PS:then
返回一个新的 Promise,并且会用这个 Promise 去 resolve
返回值,这个概念需要大家先了解一下。
根据 Promise A+ 规范
根据规范 2.3.2,如果 resolve
了一个 Promise,需要为其加上一个 then
并 resolve
。
if (x instanceof MyPromise) { if (x.currentState === PENDING) { } else { x.then(resolve, reject); } return; }复制代码
上述代码节选自手写 Promise 实现。
那么根据 A+ 规范来说,如果我们在 then
中返回了 Promise.resolve
的话会多入队一次微任务,但是这个结论还是与实际不符的,因此我们还需要寻找其他权威的文档。
根据 ECMA - 262 规范
根据规范 25.6.1.3.2,当 Promise resolve
了一个 Promise 时,会产生一个NewPromiseResolveThenableJob,这是属于 Promise Jobs 中的一种,也就是微任务。
This Job uses the supplied thenable and its then method to resolve the given promise. This process must take place as a Job to ensure that the evaluation of the then method occurs after evaluation of any surrounding code has completed.
并且该 Jobs 还会调用一次 then
函数来 resolve Promise
,这也就又生成了一次微任务。
这就是为什么会触发两次微任务的来源。
最后
文章到这里就完结了,大家有什么疑问都可以在评论区提出。
想了解更多编程学习,敬请关注php培训栏目!
以上是需要真正明白的Promise的详细内容。更多信息请关注PHP中文网其他相关文章!

热AI工具

Undresser.AI Undress
人工智能驱动的应用程序,用于创建逼真的裸体照片

AI Clothes Remover
用于从照片中去除衣服的在线人工智能工具。

Undress AI Tool
免费脱衣服图片

Clothoff.io
AI脱衣机

Video Face Swap
使用我们完全免费的人工智能换脸工具轻松在任何视频中换脸!

热门文章

热工具

记事本++7.3.1
好用且免费的代码编辑器

SublimeText3汉化版
中文版,非常好用

禅工作室 13.0.1
功能强大的PHP集成开发环境

Dreamweaver CS6
视觉化网页开发工具

SublimeText3 Mac版
神级代码编辑软件(SublimeText3)

在日常生活中,我们常常会遇到承诺与兑现之间的问题。无论是在个人关系中,还是在商业交易中,承诺的兑现都是建立信任的关键。然而,承诺的利与弊也常常会引起争议。本文将探讨承诺的利与弊,并给出一些建议,如何做到言出必行。承诺的利是显而易见的。首先,承诺可以建立信任。当一个人信守承诺时,他会让别人相信自己是一个可信赖的人。信任是人与人之间建立起的纽带,它可以让人们更加

Vue是一款流行的前端框架,在开发应用时经常会遇到各种各样的错误和问题。其中,Uncaught(inpromise)TypeError是常见的一种错误类型。在本篇文章中,我们将探讨它的产生原因和解决方法。什么是Uncaught(inpromise)TypeError?Uncaught(inpromise)TypeError错误通常出现在

Promise.resolve()详解,需要具体代码示例Promise是JavaScript中一种用于处理异步操作的机制。在实际开发中,经常需要处理一些需要按顺序执行的异步任务,而Promise.resolve()方法就是用来返回一个已经Fulfilled状态的Promise对象。Promise.resolve()是Promise类的一个静态方法,它接受一个

浏览器兼容性:哪些浏览器能够支持Promise?随着Web应用程序的复杂性不断提高,开发人员们迫切需要解决JavaScript中的异步编程问题。过去,开发人员通常使用回调函数来处理异步操作,但这会导致代码复杂和难以维护。为了解决这个问题,ECMAScript6引入了Promise,它提供了一种更直观、更灵活的处理异步操作的方式。Promise是一种用于处理异

利用Promise对象,把普通函数改成返回Promise的形式,解决回调地狱的问题。明白Promise的成功失败调用逻辑,可以灵活的进行调整。理解核心知识,先用起来,慢慢整合吸收知识。

promise对象状态有:1、pending:初始状态,既不是成功,也不是失败状态;2、fulfilled:意味着操作成功完成;3、rejected:意味着操作失败。一个Promise对象一旦完成,就会从pending状态变为fulfilled或rejected状态,且不能再改变。Promise对象在JavaScript中被广泛使用,以处理如AJAX请求、定时操作等异步操作。

Promise是一种用于处理异步操作的编程模式,它是一种代表了异步操作最终完成或失败的对象,可以看作是对异步操作的一种承诺,它可以更好地管理和组织异步代码,使得代码更加可读性高、可维护性强。Promise对象有三个状态:pending、fulfilled和rejected。Promise的核心思想是将异步操作从回调函数中分离出来,通过链式调用的方式来表达异步操作之间的依赖关系。
