前端JavaScript基础训练系列一百六十九:Promise.all([ .. ])

前端JavaScript基础训练系列一百六十九:Promise.all([ .. ])

从 then(…) 调用返回的 promise 没有调用 defer(),也没有关联错误处理函数,所 Promise | 211

以如果它(从内部或决议处理函数)拒绝的话,就会作为一个未捕获错误被报告到开发 者终端。

这种设计就是成功的坑。默认情况下,所有的错误要么被处理要么被报告,这几乎是绝大 多数情况下几乎所有开发者会期望的结果。你要么必须注册一个处理函数要么特意选择退 出,并表明你想把错误处理延迟到将来。你这时候是在为特殊情况主动承担特殊的责任。

这种方案唯一真正的危险是,如果你 defer() 了一个 Promise,但之后却没有成功查看或处 理它的拒绝结果。
但是,你得特意调用 defer() 才能选择进入这个绝望的陷阱(默认情况下总是成功的坑)。 所以这是你自己的问题,别人也无能为力。

我认为 Promise 错误处理还是有希望的(后 ES6)。我希望权威组织能够重新思考现状,考 虑一下这种修改。同时,你也可以自己实现这一点(这是一道留给大家的挑战性习题!), 或者选择更智能的 Promise 库为你实现!
这个错误处理 / 报告的精确模板是在我的 asynquence Promise 抽象库中实现 的。本部分的附录 A 中详细讨论了这个库。

Promise 模式

前文我们无疑已经看到了使用 Promise 链的顺序模式(this-then-this-then-that 流程控制), 但是可以基于 Promise 构建的异步模式抽象还有很多变体。这些模式是为了简化异步流程 控制,这使得我们的代码更容易追踪和维护,即使在程序中最复杂的部分也是如此。
原生 ES6 Promise 实现中直接支持了两个这样的模式,所以我们可以免费得到它们,用作 构建其他模式的基本块。

Promise.all([ … ])

在异步序列中(Promise 链),任意时刻都只能有一个异步任务正在执行——步骤 2 只能在 步骤 1 之后,步骤 3 只能在步骤 2 之后。但是,如果想要同时执行两个或更多步骤(也就 是“并行执行”),要怎么实现呢?

在经典的编程术语中,门(gate)是这样一种机制要等待两个或更多并行 / 并发的任务都 完成才能继续。它们的完成顺序并不重要,但是必须都要完成,门才能打开并让流程控制 继续。

在 Promise API 中,这种模式被称为 all([ … ])。
假定你想要同时发送两个 Ajax 请求,等它们不管以什么顺序全部完成之后,再发送第三个 Ajax 请求。考虑:

// request(..)是一个Promise-aware Ajax工具
// 就像我们在本章前面定义的一样
var p1 = request( "http://some.url.1/" );
     var p2 = request( "http://some.url.2/" );
     Promise.all( [p1,p2] )
     .then( function(msgs){
// 这里,p1和p2完成并把它们的消息传入 return request(
             "http://some.url.3/?v=" + msgs.join(",")
         );
})
.then( function(msg){
         console.log( msg );
     } );

Promise.all([ … ])需要一个参数,是一个数组,通常由Promise实例组成。从Promise. all([ … ]) 调用返回的 promise 会收到一个完成消息(代码片段中的 msg)。这是一个由所 有传入 promise 的完成消息组成的数组,与指定的顺序一致(与完成顺序无关)。

严格说来,传给 Promise.all([ … ]) 的数组中的值可以是 Promise、 thenable,甚至是立即值。就本质而言,列表中的每个值都会通过 Promise. resolve(…) 过滤,以确保要等待的是一个真正的 Promise,所以立即值会 被规范化为为这个值构建的 Promise。如果数组是空的,主 Promise 就会立 即完成。

从Promise.all([ … ])返回的主promise在且仅在所有的成员promise都完成后才会完 成。如果这些promise中有任何一个被拒绝的话,主Promise.all([ … ])promise就会立 即被拒绝,并丢弃来自其他所有 promise 的全部结果。
永远要记住为每个promise关联一个拒绝/错误处理函数,特别是从Promise.all([ … ]) 返回的那一个。

Promise.race([ … ])

尽管Promise.all([ … ])协调多个并发Promise的运行,并假定所有Promise都需要完
成,但有时候你会想只响应“第一个跨过终点线的 Promise”,而抛弃其他 Promise。 这种模式传统上称为门闩,但在 Promise 中称为竞态。

虽然“只有第一个到达终点的才算胜利”这个比喻很好地描述了其行为特 性,但遗憾的是,由于竞态条件通常被认为是程序中的 bug, 所以从某种程度上说,“竞争”这个词已经是一个具有固定意义的术语了。 不要混淆了 Promise.race([…]) 和竞态条件。

Promise.race([ … ])也接受单个数组参数。这个数组由一个或多个Promise、thenable或 立即值组成。立即值之间的竞争在实践中没有太大意义,因为显然列表中的第一个会获 胜,就像赛跑中有一个选手是从终点开始比赛一样!

与Promise.all([ … ])类似,一旦有任何一个Promise决议为完成,Promise.race([ … ]) 就会完成;一旦有任何一个 Promise 决议为拒绝,它就会拒绝。

一项竞赛需要至少一个“参赛者”。所以,如果你传入了一个空数组,主 race([…]) Promise 永远不会决议,而不是立即决议。这很容易搬起石头砸 自己的脚! ES6 应该指定它完成或拒绝,抑或只是抛出某种同步错误。遗憾 的是,因为 Promise 库在时间上早于 ES6 Promise,它们不得已遗留了这个 问题,所以,要注意,永远不要递送空数组。

再回顾一下前面的并发 Ajax 例子,不过这次的 p1 和 p2 是竞争关系:

// request(..)是一个支持Promise的Ajax工具
// 就像我们在本章前面定义的一样
var p1 = request( "http://some.url.1/" );
     var p2 = request( "http://some.url.2/" );
     Promise.race( [p1,p2] )
     .then( function(msg){
// p1或者p2将赢得这场竞赛 return request(
             "http://some.url.3/?v=" + msg
         );
})
.then( function(msg){
         console.log( msg );
     } );

因为只有一个 promise 能够取胜,所以完成值是单个消息,而不是像对 Promise.all([ 那样的是一个数组。

转载请说明出处内容投诉
CSS教程_站长资源网 » 前端JavaScript基础训练系列一百六十九:Promise.all([ .. ])

发表评论

欢迎 访客 发表评论

一个令你着迷的主题!

查看演示 官网购买