Promise Execute
Promise是一个对象,用作延迟(可能还有异步)计算最终结果的占位符。
Note
promise是一个值(且该值在当前状态下并不知道)的代理,当Promise创建的时候。 其允许你关联成功或者失败的操作事件。让异步的操作看起来像是同步的操作(异步方法是不会立即返回最终的值, 而是返回一个Promise, 在未来的某个时刻使用)
任何Promise都处于三种相互排斥的状态之一:fullfilled、rejected和pending:
- 如果p.then(f, r)将立即排队到任务中,立即调用
f, 则promise对象p的状态是fulfilled - 如果p.then(f, r)将立即排队到任务中,立即调用
r, 则promise对象p的状态是rejected - 如果p的状态既不是fulfilled也不是rejected,则其状态是pending
Promise Internal value of invoke constructor
当Promise的构造函数被调用时,有以下两件事触发:
- A Promise Object is created
此Promise对象内部有以下插槽:
[[PromiseState]][[PromiseResult]][[PromiseIsHandled]][[PromiseFullfillReactions]][[PromiseRejectReactions]]
- A Promise capability record is created
它封装了Promise,并且添加了一些额外的函数来resolve或者rejectPromise,这些函数控制了Promise最终的
[[PromiseState]]和[[PromiseResult]],并且启动了异步任务。
通过调用resolve来resolve Promise,可以通过调用executor函数来获取。
当调用resolve函数时:
[[PromiseState]]由pending设置为fulfilled[[PromiseResult]]设置为done[[PromiseIsHandled]]设置为false
Promise Internal value of invoke then
当调用then函数时,[[PromiseFulfillReactions]]和[[PromiseRejectReactions]]在其中发挥着作用。
[[PromiseFulfillReactions]] field包含Promise Reaction,这是一个通过将then处理程序链接到promise而创建的对象。
Promise Reaction的属性[[Handler]]指向了then中的回调函数,当Promise被resolve,此回调函数被加入了
Microtask Queue,并且可以访问和解析Promise的值。
当Promise resolves时,这个处理回调函数接收[[PromiseResult]]的值作为其参数,之后它被推送到微任务队列。
Note
微任务队列是一个特殊的队列在事件循环中,当调用栈是空的,事件循环程序中首先会处理为任务队列中的任务,直到微任务队列为空, 然后再处理任务队列(宏任务队列或者回调队列)
同样的,也可以创建Promise Reaction record 来处理Promise rejection,通过catch,此catch的参数回调函数也会被
加入微任务队列。
到目前为止,我们只在executor函数中直接调用了resolve或reject。虽然这是可能的,但它并没有充分利用Promise的力量(和主要目的)!
在大多数情况下,我们会想要在某个稍后的时间点resolve或reject,通常是在异步任务完成时。
这里我们使用setTimeout来模拟异步任务。
new Promise((resolve,reject) => {setTimeout(() => {resolve('done')}, 1000);}).then(result => console.log(result))
首先,new Promise构造函数加入到了调用栈,并且创建了Promise 实例
然后,executor函数执行了,调用了setTimeout, 其被加入到了调用栈。
setTimeout负责调度Timers Web API中的计时器,延迟为1000ms,之后我们传递给setTimeout的回调将被推送到任务队列。
在计时器和Promise构造函数从调用堆栈中弹出后,引擎会遇到then。
then被加入到了调用栈,并且创建了一个Promise Reaction record,其属性[[handler]]指向了then的回调函数。
由于[[PromiseState]]仍是"pending",Promise Reaction record被加入到了[[PromiseFulfillReactions]]列表中。
当1000ms到达时,setTimeout回调函数被加入到任务队列中。
此时调用栈中如果有其他任务,则执行其他任务,直到此setTimeout的回调,此时回调会从任务队列中进入到调用栈,立即执行回调。
此时,会执行resolve,[[PromiseState]]设置为"fulfilled",[[PromiseResult]]设置为"done",此时
跟Promise Reaction record相关联的代码被加入到了微任务队列,然后调用栈弹出了回调函数和resolve。
由于调用堆栈是空的,事件循环首先检查then处理程序的回调正在等待的微任务队列。
然后then回调函数进入调用栈,打印result的值,也就是Promise内部插槽属性[[PromiseResult]]的值"done"。
一旦回调完成执行并从调用堆栈中弹出,程序就完成了!
Promise Internal value of multi then
除了创建Promise reaction record之外,then也会返回一个Promise,也就意外着可以将多个then进行链式调用。
new Promise((resolve,reject) => {resolve(1)}).then(result => result * 2).then(result => result * 2).then(result => console.log(result))
当此代码执行时,调用Promise构造函数时会创建一个Promise对象。之后,每当引擎遇到then时,
都会创建Promise Reaction record和Promise对象。
在这两种情况下,then回调都将接收到的[[PromiseResult]]值乘以2。then的[[PromiseResult]]被设置为该计算的结果,
该结果又被下一个then的处理程序使用。
最终,结果被打印出来了。最后一个then promise的[[PromiseResult]]是undefined,因为我们没有显式返回值,这意味着它隐式返回了未定义的值。
Conclusion
Promise是一个具有一些额外的功能来改变其内部状态的对象。
而这些额外的功能也就是最酷的事情,如果其被then或者catch附加的话,是可以触发异步的操作。
由于这些处理程序被添加到微任务队列中,因此你可以以非阻塞的方式处理这些最终的结果。处理错误也是
容易的。将多个处理程序链接到一起,可以使你的代码具有可读性可可维护性!