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

每个 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 独有的局限性,但可能是我们希望绕过的陷阱。

  • 22
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
前端面试中,Promise.all() 是一个常见的问题。它是一个用于处理多个 Promise 对象的方法,返回一个新的 Promise 对象。根据引用所述,Promise.all() 方法接收一个 promise 的 iterable 类型的输入(例如数组),并且只返回一个 Promise 实例。当所有输入的 promise 都成功执行时,Promise.all() 返回的 Promise 对象的 resolve 回调会传递一个包含所有 promise 结果的数组。如果输入的 promise 中有任何一个失败或者输入不合法的 promise,Promise.all() 返回的 Promise 对象的 reject 回调会立即抛出错误,并且 reject 的是第一个抛出的错误信息[^2]。 以下是一个示例,演示了如何使用 Promise.all() 方法: ```javascript const promise1 = Promise.resolve(3); const promise2 = 42; const promise3 = new Promise((resolve, reject) => { setTimeout(resolve, 100, 'foo'); }); Promise.all([promise1, promise2, promise3]) .then(values => { console.log(values); // 输出:[3, 42, 'foo'] }) .catch(error => { console.error(error); // 如果有任何一个 promise 失败,会立即抛出错误 }); ``` 在上面的示例中,我们创建了三个 Promise 对象:promise1、promise2 和 promise3。promise1 是一个已经解决的 Promise,promise2 是一个普通的值,promise3 是一个在 100 毫秒后解决的 Promise。我们将这三个 Promise 对象作为参数传递给 Promise.all() 方法。当所有的 Promise 都成功执行时,Promise.all() 返回的 Promise 对象的 resolve 回调会传递一个包含所有 promise 结果的数组。在这个示例中,我们通过 then() 方法获取到了这个数组,并将其打印出来。如果有任何一个 Promise 失败,Promise.all() 返回的 Promise 对象的 reject 回调会立即抛出错误,并且 reject 的是第一个抛出的错误信息。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值