为什么 Promises 的结果看起来不正确?

huangapple go评论127阅读模式
英文:

why does the Promises result looks like incorrect?

问题

Promise.resolve(1)
    .then(x => console.log(1))
    .catch(x => console.log(2))
    .then(x => console.log(3))

Promise.reject(2)
    .then(x => console.log(4))
    .then(x => console.log(5))
    .catch(x => console.log(6))
    .then(x => console.log(7))

结果是:1 3 6 7

您尝试理解的是JavaScript中Promise的工作原理。首先,让我们解释一下代码的执行顺序:

  1. Promise.resolve(1)创建了一个已解决(resolved)的Promise,它的.then会在下一个微任务队列中执行,打印1。

  2. .catch(x => console.log(2))位于已解决的Promise链上,但在前面没有拒绝(rejected)的Promise,所以不会执行,跳过。

  3. .then(x => console.log(3))会在下一个微任务队列中执行,打印3。

  4. Promise.reject(2)创建了一个已拒绝(rejected)的Promise,它的.catch会捕获拒绝,打印6。

  5. .then(x => console.log(4)).then(x => console.log(5))在已拒绝的Promise链上,但是它们之前有一个拒绝的Promise,所以它们不会执行,跳过。

  6. .catch(x => console.log(6))捕获拒绝,打印6。

  7. .then(x => console.log(7))会在下一个微任务队列中执行,打印7。

由于Promise的执行是异步的,所以最终的结果是1 3 6 7。

如果您在第二个已解决的Promise之前添加了一个.then(x => console.log(5)),那么它将在下一个微任务队列中执行,结果变成1 3 5 6 7。这是因为Promise的.then.catch都会创建一个新的Promise链,它们之间没有直接的影响。

英文:
Promise.resolve(1)
    .then(x => console.log(1))
    .catch(x => console.log(2))
    .then(x => console.log(3))

Promise.reject(2)
    .then(x => console.log(4))
    .catch(x => console.log(6))
    .then(x => console.log(7))

result is "1 6 3 7", but if i add another then() before catch with console.log(6) result will be 1 3 6 7, i dont't understand why

Promise.resolve(1)
    .then(x => console.log(1))
    .catch(x => console.log(2))
    .then(x => console.log(3))

Promise.reject(2)
    .then(x => console.log(4))
    .then(x => console.log(5))
    .catch(x => console.log(6))
    .then(x => console.log(7))

result is 1 3 6 7

i'm trying to understand how it works

答案1

得分: 1

承诺监听器由一个承诺任务运行程序作业来运行,当承诺变得实现或被拒绝时,会将其放入承诺作业队列中。HTML 5 使用微任务队列作为承诺作业队列,并且该队列按先进先出(FIFO)的方式运作。

承诺作业会连续运行,在其他 JavaScript 返回到事件循环之后,直到承诺作业队列为空。

承诺作业的目的是将创建作业的承诺的值或拒绝原因传递给承诺链中的下一个承诺。

如果创建作业的承诺被实现,下一个承诺将以相同的值被实现,如果被拒绝,将以相同的原因被拒绝。如果承诺监听器返回一个承诺,下一个承诺将用返回的承诺解决。

将承诺状态和值传递给下一个承诺是使用其内部存储的 resolvereject 函数来执行的。

值得注意的是,catch 子句是对 then 的两个参数调用的简写。无论源代码中的子句是 then 还是 catch,都会创建承诺作业来解决、实现或拒绝子句返回的承诺对象。

将所有这些结合在一起应该能够在同时处理多个承诺链的情况下预测承诺监听器执行的顺序。

第一个示例在以下顺序中交织了承诺作业:

  1. log(1),不是 log(4)
  2. 不是 log(2),log 6
  3. log(3),log(7)

第二个示例也交织了程序作业,但对齐方式不同:

  1. log(1),不是 log(4)
  2. 不是 log(2),不是 log(5)
  3. log(3),log(6)
  4. log(7)

故事的寓意?如果你需要部分完成结果,不要依赖于承诺队列中多个作业的执行顺序,可以将承诺链分成更小的链。

英文:

Promise listeners are run by a Promise task runner job put in the Promise Job Queue when a promise becomes fulfilled or rejected. HTML 5 uses the microtask queue for the Promise Job Queue, and the queue operates as a FIFO (first in first out).

Promise Jobs are run consecutively, after other JavaScript has returned to the event loop, until the Promise Job queue is empty.

The purpose of a promise job is to pass the value or rejection reason of the promise that created the job onto the next promise in a chain of promises.

If the promise spawning the job is fulfilled, the next promise will be fulfilled with the same value, and if rejected, rejected with the same reason. If a promise listener returns a promise, the next promise is resolved with the promise returned.

Passing on promise state and value to next promises is performed using internally stored¹ values of their resolve and reject functions.

It's important to note that catch clauses are shorthand² for a two argument call to then. Whether a clause in the source code says then or catch, promise jobs are still created to resolve, fulfill or reject the promise object that the clause returned.

Putting all this together should allow predicting the sequence of promise listener executions in cases when multiple promise chains are being processed at the same time.

The first example inter-meshes promise jobs in the following sequence:

  1. log(1), not log(4)
  2. not log(2), log 6
  3. log(3), log(7)

The second example also inter-meshes program jobs but with a different alignment:

  1. log(1), not log(4)
  2. not log(2), not log(5)
  3. log(3), log(6)
  4. log( 7)

Moral of the story? Don't depend on the execution order of multiple jobs in the promise queue, by breaking promise chains into smaller chains if you need partial completion results.


¹ About resolve and reject functions for chained promises.

  • Promise objects internally store the resolve and reject functions for promises returned by calling their then, catch and finally methods.
  • Promise jobs use these stored functions to resolve, fulfill or reject next promises in a chain.
  • the stored functions internally reference the promises they were issued for. Typically this is what holds chained promises in memory and prevents them being garbage collected before settlement.

² About then calls with one argument

  • then(listenerFunction) is equivalent to then(listenerFunction, null). Applied to a rejected promise, null as the rejection handler causes the next promise in a chain to be rejected with the same reason.
  • catch(listenerFunction) is equivalent to then(null, listenerFunction). Applied to a fulfilled promise, null as the fulfillment handler causes the next promise in a chain to be fulfilled with the same value.

Promise chain clauses that don't apply to the settled state of a previous promise in a chain are not skipped - they actively pass on the state and value of the previous promise to the next promise within a promise job entered into the microtask queue for that purpose.

答案2

得分: 0

你第一次调用了 Promise.resolve,因此 then 回调将被调用。任何后续的 then 回调也将被调用。

接下来,你调用了 Promise.reject,这意味着直到 catch 回调(拒绝处理程序)之前,每个 then 回调都将被忽略。然而,一旦 catch 处理程序完成解析(或完成返回某些内容),如果它没有抛出错误,下一个 then 回调将被调用。

英文:

You called Promise.resolve the first time, so the then callback will be called. Any subsequent then callbacks will also be called.

Next, you called Promise.reject that means that every then callback will be ignored up to the catch callback (the rejection handler). However, once the catch handler is finished resolving (or finished returning something), if it didn't throw an error, the next then callback is called.

huangapple
  • 本文由 发表于 2023年5月24日 22:07:25
  • 转载请务必保留本文链接:https://go.coder-hub.com/76324434.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定