在此之前和之后放置渔获物


103

我很难理解将.catchBEFORE和AFTER放在嵌套诺言中的区别。

选择1:

test1Async(10).then((res) => {
  return test2Async(22)
    .then((res) => {
      return test3Async(100);
    }).catch((err) => {
      throw "ERROR AFTER THEN";
    });
}).then((res) => {
  console.log(res);
}).catch((err) => {
  console.log(err);
});

选择2:

test1Async(10).then((res) => {
   return test2Async(22)
     .catch((err) => {
        throw "ERROR BEFORE THEN";
      })
      .then((res) => {
        return test3Async(100);
      });
  }).then((res) => {
    console.log(res);
  }).catch((err) => {
    console.log(err);
  });

每个函数的行为如下,如果号码是test1的失败<0,如果号码是test2的失败> 10,如果数量不TEST3失败100。在这种情况下,test2仅失败。

我尝试运行并使test2Async失败,然后在执行之前和之后都以相同的方式运行,并且未执行test3Async。有人可以向我解释在不同地方放置渔获物的主要区别吗?

在每个函数中,我console.log('Running test X')为了检查它是否被执行。

出现此问题的原因是我发布的上一个线程如何将嵌套回调转换为Promise?。我认为这是一个不同的问题,值得发表另一个话题。


.then和.catch都可以改变承诺...所以我不确定误解是从哪里来的。如果将catch放在.then之前,则它将捕获在.then之前发生的拒绝,然后.then将根据.catch中发生的情况运行完成/失败回调,反之亦然。
凯文B

对不起,如果我的问题不清楚。但是在这种情况下,正如我所说的,两种情况的行为都相同,所以我看不到区别。你能告诉我什么时候我们把钓钩放到什么时候,再什么时候再决定放它吗?把它放在后面似乎真的很直观和普遍。只是不确定为什么我们有时在此之前放它
Zanko '17

如果它们执行相同的操作,那仅仅是因为在这种特定情况下,每个操作都不会改变结果。对其中任何一个较小的更改都可能改变结果。
凯文

你是什​​么意思“改变结果”。抱歉,我真的很困惑哈哈
Zanko '17

例如,如果您没有做任何错误而不是抛出错误,那么诺言就会从被拒绝变为解决。这当然会改变结果,因为诺言现在是解决的诺言,而不是被拒绝的诺言。(当然,除非已经解决,否则无论如何都不会捕获)
Kevin B

Answers:


237

因此,基本上,您要问的是这两者之间有什么区别(哪里p是以前的代码创建的promise):

return p.then(...).catch(...);

return p.catch(...).then(...);

p解析或拒绝时存在差异,但是这些差异是否重要取决于.then().catch()处理程序中的代码做什么。

p解决时会发生什么:

在第一种方案中,当p解析时,将.then()调用处理程序。如果该.then()处理程序返回一个值或另一个最终解决的承诺,则将.catch()跳过该处理程序。但是,如果.then()处理程序抛出或返回最终拒绝的promise,则.catch()处理程序将执行原始promise中的拒绝操作,同时执行处理程序p中发生的错误.then()

在第二种方案中,当p解析时,将.then()调用处理程序。如果该.then()处理程序抛出或返回最终拒绝的承诺,则该.catch()处理程序将无法捕获该承诺,因为它在链中之前。

因此,这是差异1。如果.catch()处理程序是AFTER之后的,那么它也可以捕获.then()处理程序内部的错误。

p拒绝时会发生什么:

现在,在第一个方案中,如果Promise p拒绝,则.then()跳过.catch()处理程序,并按您期望的方式调用处理程序。您在.catch()处理程序中执行的操作将确定返回的最终结果。如果您只是从.catch()处理程序中返回一个值或返回最终可以解决的承诺,那么由于您已“处理”错误并正常返回,因此承诺链会切换到已解决状态。如果在.catch()处理程序中抛出或返回被拒绝的诺言,则返回的诺言将保持被拒绝状态。

在第二种方案中,如果Promise p拒绝,则.catch()调用处理程序。如果您返回一个正常值或最终从.catch()处理程序中解决的承诺(因此“处理”错误),则承诺链将切换到已解决状态,并且将在调用.then()处理程序.catch()

这就是差异2。如果.catch()处理程序在BEFORE之前,则它可以处理错误并允许.then()处理程序继续被调用。

何时使用:

如果只希望一个.catch()处理程序可以捕获原始诺言p或处理程序中的错误,.then()而拒绝来自p.then()处理程序应跳过该处理程序,则使用第一种方案。

如果您希望能够捕获原始promise中的错误,p并且可以(取决于条件)允许promise链继续解析,从而执行.then()处理程序,请使用第二种方案。

另一种选择

还有一个选项可以使用两个回调,您可以像下面这样传递.then()

 p.then(fn1, fn2)

这样可以保证仅会调用fn1fn2之一。如果p解决,fn1则将被调用。如果p拒绝,fn2则将被调用。结果的任何改变fn1都不会引起别人的注意,fn2反之亦然。因此,如果您想绝对确保无论处理程序本身发生了什么,都将仅调用两个处理程序之一,则可以使用p.then(fn1, fn2)


17
现在的问题是关于具体的顺序.then().catch(),你回答。此外,您还提供了有关何时使用哪种顺序的提示,我认为应该提到第三个选项,即将成功处理程序和错误处理程序都传递给.then()。在这种情况下,最多将调用一个处理程序。
ArneHugo

7
@ArneHugo-好建议。我加了
jfriend00

那么,在Promise Chaining中,我们可以编写.then .catch .catch .then哪种情况吗?
卡皮尔·拉格万希

@KapilRaghuwanshi,是的,如果发生故障,您可以使用它传递默认值。即Promise.reject(new Error("F")).then(x => x).catch(e => {console.log(e); return [1]}).then(console.log)Promise.resolve([2]).then(x => x).catch(e => [1]).then(console.log)
CervEd

1
@DmitryShvedov-如我所猜,这是错误的.then(this.setState({isModalOpen: false}))。您没有传递一个函数引用,.then()以便使parens中的代码立即执行(在promise解决之前)。应该是.then(() => this.setState({isModalOpen: false}))
jfriend00

31

jfriend00的答案很好,但我认为添加类似的同步代码将是一个好主意。

return p.then(...).catch(...);

与同步类似:

try {
  iMightThrow() // like `p`
  then()
} catch (err) {
  handleCatch()
}

如果iMightThrow()不抛出,then()将被调用。如果确实抛出(或者then()本身抛出),handleCatch()则将被调用。注意,该catch块如何无法控制是否then被调用。

另一方面,

return p.catch(...).then(...);

与同步类似:

try {
  iMightThrow()
} catch (err) {
  handleCatch()
}

then()

在这种情况下,如果iMightThrow()不抛出,then()则将执行。如果确实抛出,则将handleCatch()决定是否then()调用,因为如果handleCatch()重新抛出,则then()不会被调用,因为异常将立即抛出给调用者。如果handleCatch()可以正常处理问题,then()则将被调用。


这是一个很好的解释,但是您可以将孤儿包裹then()finally{...}
tyskr

2
@ 82Tuskers,您确定吗?如果我then()输入finally{...},即使handleCatch()抛出也会被错误地调用吗?请记住,我的目标是显示类似的同步代码,而不是提出处理异常的不同方法
akivajgordon

因此,如果我们想处理所有情况但仍然链接.then(),则最好使用.then(做某事).catch(记录错误并更新状态).then(做另一件事).catch(log err)我们在哪里试图抓住每一个点,但又继续执行stmnts?
安娜
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.