问题 promise链如何引用下一个错误处理程序


我想知道是否有人知道promise链如何引用下一个错误处理程序 - 例如:

  const p = new Promise(resolve => resolve(5))
   .then(v => 5*v)
   .then(v => { throw 'foo' });

  p.then(v => v/4)
   .then(v => v+3)
   .catch(e => console.error('first catch:', e));

  p.then(v => v/4)
   .then(v => v+3)
   .catch(e => console.error('second catch:', e));

如果你跑了,你会得到:

first catch: foo
second catch: foo

据我所知,每一次 Promise.prototype.then 是一个被调用的,创建并返回一个新的promise。我能想到能够找到的承诺的唯一方法 下一个错误处理器 对于每个链,都有一个对子项的引用数组。因此,如果承诺被拒绝,它将通过所有孩子并在每个子链中找到最接近的拒绝处理程序。

有谁知道这是如何实现的?


11929
2018-06-16 02:29


起源

const p是一个被拒绝的承诺 - 你知道吗?重读问题,我想你可能会这样做 - 抱歉 - Jaromanda X
您要问的是实现细节,这取决于哪个浏览器(对于本机承诺)或哪个库(对于第三方承诺)。但是,一般而言,承诺需要跟踪他们的直系子女,以便他们在解决或拒绝时通知他们。他们不需要跟踪几个分离度的处理程序,因为每个 .then 要么 .catch 用自己的“孩子”创造新的承诺。 - JLRishe


答案:


我看到它的方式:

将promises视为嵌套数组:

[then1,[then1.1, then1.2, catch1.1, then1.3, then1.4], then2, catch1]

看起来像这样:

new Promise(...)
.then(() => {         // <- 1
  return Promise(...)
          .then()     // <- 1.1
          .then()     // <- 1.2
          .catch()    // <- 1.1
          .then()     // <- 1.3
          .then()     // <- 1.4
})
.then()               // <- 2
.catch()              // <- 1

现在假设我们开始执行,它从最顶层的数组开始。我们为每个数组都有一个索引,指定我们在执行方面正在等待哪个元素。 我们先打电话来 .then1,它本身返回一个promise链(另一个数组)。当出现错误时,层次结构数组中最低的(最深的) 跳过(不执行) 它是元素,直到找到一个 catch。如果是,则执行 catch 并继续执行其他元素。如果没找到 catch 它要求父数组查找并执行a catch,跳过所有元素 包括它的子数组,因为它们没有捕获

在我们的示例中,如果发生错误 then1.2 它会被抓住 catch1.1,但如果它发生在 then1.3 它会一直传播到 catch1 跳绳 then1.4 和 then2

编辑:

以下是要试验的代码:

new Promise(res => res())
.then(() => 
    new Promise(res => res())
    .then(() => console.log(1))
    .then(() => {console.log(2); throw "Error 1"})
    .catch((err) => console.log(err))
    .then(() => {console.log(3); throw "Error 2"}))
    .then(() => console.log(4))
.then(() => 
    new Promise(res => res())
    .then(() => console.log(6))
    .then(() => {console.log(7); throw "Error 3"})
    .catch((err) => console.log(err))
    .then(() => console.log(8))
    .then(() => {console.log(9); throw "Error 4"}))
.then(() => console.log(10))
.catch((err) => console.log(err))

它记录:

1

2

错误1

3

错误2


8
2018-06-20 20:14



是的,它就像一棵树,每个承诺引用它的孩子。 - Alexander Mills
这不完全正确。 Promise不是嵌套数组,它们是嵌套实体/上下文。我将在这里省略对分辨率上下文的引用,但它们更接近于以下示例 Promise.resolve(Promise.resolve(2)) 实际上是一个 Promise(Promise(2))。 Promise.resolve(2).catch(console.log)`实际上是一个 Catch(Promise(2))。 Promise.resolve(2).then(() => Promise.reject(new Error('2')).catch(console.log) 是 Catch(Then(Promise)) - Robert Mennell
更短的流行语:它的构成,而不是嵌套 - Robert Mennell
@RobertMennell我从未说过它们是由嵌套数组实现的,只是做了一个类比,可以帮助同事更轻松地理解它:) - Alexander Vitanov
除了踩过这个例子后,我看到了提问者想要澄清的同样错误。第一个问题是没有开启 1.1 如你所说。它实际上是开启的 1.2。如果我支持新的承诺 1.1 并抛出,该捕获将不会收到该链上的错误。 - Robert Mennell


所以,让我们先介绍一下你的例子。

const p = new Promise(resolve => resolve(5))
 .then(v => 5*v)
 .then(v => { throw 'foo' });

p.then(v => v/4)
 .then(v => v+3)
 .catch(e => console.error('first catch:', e));

p.then(v => v/4)
 .then(v => v+3)
 .catch(e => console.error('second catch:', e));

在这里你实际上有3个不同的承诺:

p 是的 Promise.then.thenanonymous 是的 p.then.then.catch,和 anonymous 是的 p.then.then.catch

记住这一点 Promise.then 和 Promise.catch 返回包含之前Promise的Promises。所以你最终得到的是与这三个承诺链的嵌套承诺。

什么时候 p 最终投入评估的第三个承诺,其他两个承诺现在检查返回类型,标志和其他内部信息,最终导致他们实现从链中较早的承诺被拒绝,因此它必须拒绝。您还可以使用以下方法模拟此:

var p = Promise.reject(new Error('Hi from p!'))

p.catch(e => console.log('Hello from a different promise', p))
==
p.catch(e => console.log('Hello from a yet different promise', p))

您应该注意到评估的返回值为false,这意味着两个对象不相等。

现在如果我们稍等一下然后附加一个新的catch处理程序会发生什么 p

setTimeout(() => p.catch(console.log), 500)

您应该注意到另一个控制台日志,这次只有'Hi from p!'

原因是Promise评估的顺序。它评估创建,如果它是在被拒绝的promise上创建的,它会评估为拒绝并转到catch处理程序。

您认为以下是什么?

Promise
  .reject(new Error('1'))
  .catch(console.log)
  .then(() => Promise.reject(new Error('2')))
  .then(() => new Error('3'))
  .then(console.log)
  .catch(console.log)
  .catch(() => console.log('4'))

这是正确的,你会看到打印错误('1'),然后是打印错误('2'),但不是打印错误('3')或打印'4'。那是因为a .catchpromise执行以下两项操作之一:解析对已解析值的承诺,或者在链被拒绝时解析对函数值的承诺。

相反 .then 只能解决一个承诺。如果它的分辨率被拒绝,它将以分辨率拒绝。

这有点令人困惑,但是一旦你意识到承诺会等待解决,并且会检查和链接承诺,你就会更容易找出你拒绝的地方。


2
2018-06-20 20:39