前端JavaScript基础训练系列一百七十二:then(..) 和 catch(..)

作者 : admin 本文共2448个字,预计阅读时间需要7分钟 发布时间: 2024-02-1 共2人阅读

每个 Promise 实例(不是 Promise API 命名空间)都有 then(…) 和 catch(…) 方法,通过 这两个方法可以为这个 Promise 注册完成和拒绝处理函数。Promise 决议之后,立即会调用 这两个处理函数之一,但不会两个都调用,而且总是异步调用。

then(…) 接受一个或两个参数:第一个用于完成回调,第二个用于拒绝回调。如果两者中 的任何一个被省略或者作为非函数值传入的话,就会替换为相应的默认回调。默认完成回 调只是把消息传递下去,而默认拒绝回调则只是重新抛出(传播)其接收到的出错原因。

就像刚刚讨论过的一样,catch(…) 只接受一个拒绝回调作为参数,并自动替换默认完成 回调。换句话说,它等价于 then(null,…):

p.then( fulfilled );
p.then( fulfilled, rejected );
p.catch( rejected ); // 或者p.then( null, rejected )

then(…) 和 catch(…) 也会创建并返回一个新的 promise,这个 promise 可以用于实现 Promise 链式流程控制。如果完成或拒绝回调中抛出异常,返回的 promise 是被拒绝的。如 果任意一个回调返回非 Promise、非 thenable 的立即值,这个值会被用作返回 promise 的完 成值。如果完成处理函数返回一个 promise 或 thenable,那么这个值会被展开,并作为返回 promise 的决议值。

Promise.all([ … ]) 和 Promise.race([ … ])

ES6PromiseAPI静态辅助函数Promise.all([ … ])和Promise.race([ … ])都会创建一
个 Promise 作为它们的返回值。这个 promise 的决议完全由传入的 promise 数组控制。

对Promise.all([ … ])来说,只有传入的所有promise都完成,返回promise才能完成。 如果有任何 promise 被拒绝,返回的主 promise 就立即会被拒绝(抛弃任何其他 promise 的 结果)。如果完成的话,你会得到一个数组,其中包含传入的所有 promise 的完成值。对于 拒绝的情况,你只会得到第一个拒绝 promise 的拒绝理由值。这种模式传统上被称为门: 所有人都到齐了才开门。

对Promise.race([ … ])来说,只有第一个决议的promise(完成或拒绝)取胜,并且其 决议结果成为返回 promise 的决议。这种模式传统上称为门闩:第一个到达者打开门闩通 过。考虑:

    var p1 = Promise.resolve( 42 );
     var p2 = Promise.resolve( "Hello World" );
     var p3 = Promise.reject( "Oops" );
 Promise.race( [p1,p2,p3] )
.then( function(msg){
    console.log( msg );
} );
Promise.all( [p1,p2,p3] )
.catch( function(err){
    console.error( err );
} );
Promise.all( [p1,p2] )
.then( function(msgs){
    console.log( msgs );
} );
// 42
// "Oops"
// [42,"Hello World"]

当心!若向Promise.all([ … ])传入空数组,它会立即完成,但Promise. race([ … ]) 会挂住,且永远不会决议。
ES6 Promise API 非常简单直观。它至少足以处理最基本的异步情况,并且如果要重新整 理,把代码从回调地狱解救出来的话,它也是一个很好的起点。
但是,应用常常会有很多更复杂的异步情况需要实现,而 Promise 本身对此在处理上具有 局限性。下一节会深入探讨这些局限,理解 Promise 库出现的动机。

Promise 局限性

顺序错误处理

本章前面已经详细介绍了适合 Promise 的错误处理。Promise 的设计局限性(具体来说,就 是它们链接的方式)造成了一个让人很容易中招的陷阱,即 Promise 链中的错误很容易被 无意中默默忽略掉。

关于 Promise 错误,还有其他需要考虑的地方。由于一个 Promise 链仅仅是连接到一起的 成员 Promise,没有把整个链标识为一个个体的实体,这意味着没有外部方法可以用于观 察可能发生的错误。

如果构建了一个没有错误处理函数的 Promise 链,链中任何地方的任何错误都会在链中一 直传播下去,直到被查看(通过在某个步骤注册拒绝处理函数)。在这个特定的例子中, 只要有一个指向链中最后一个 promise 的引用就足够了(下面代码中的 p),因为你可以在 那里注册拒绝处理函数,而且这个处理函数能够得到所有传播过来的错误的通知:

// foo(..), STEP2(..)以及STEP3(..)都是支持promise的工具
     var p = foo( 42 )
     .then( STEP2 )
     .then( STEP3 );

虽然这里可能有点鬼祟、令人迷惑,但是这里的 p 并不指向链中的第一个 promise(调用 foo(42) 产生的那一个),而是指向最后一个 promise,即来自调用 then(STEP3) 的那一个。
还有,这个 Promise 链中的任何一个步骤都没有显式地处理自身错误。这意味着你可以在 p 上注册一个拒绝错误处理函数,对于链中任何位置出现的任何错误,这个处理函数都会 得到通知:

 p.catch( handleErrors );

但是,如果链中的任何一个步骤事实上进行了自身的错误处理(可能以隐藏或抽象的不可 见的方式),那你的 handleErrors(…) 就不会得到通知。这可能是你想要的——毕竟这是 一个“已处理的拒绝”——但也可能并不是。完全不能得到(对任何“已经处理”的拒绝 错误的)错误通知也是一个缺陷,它限制了某些用例的功能。

基本上,这等同于 try…catch 存在的局限:try…catch 可能捕获一个异常并简单地吞掉 它。所以这并不是 Promise 独有的局限性,但可能是我们希望绕过的陷阱。

本站无任何商业行为
个人在线分享 » 前端JavaScript基础训练系列一百七十二:then(..) 和 catch(..)
E-->