如何使用Promise.race() 和 Promise.any() ?

开发 前端
自1996年发布以来,JS 一直在稳步改进。随着ECMAScript版本的许多改进,最近的版本是ES2020。JS 的一个重要更新是Promise,在2015年,它以 ES6 的名义发布。

 [[357642]]

自1996年发布以来,JS 一直在稳步改进。随着ECMAScript版本的许多改进,最近的版本是ES2020。JS 的一个重要更新是Promise,在2015年,它以 ES6 的名义发布。

什么是 Promise ?

MDN 上对 Promise 的定义:Promise 对象用于表示一个异步操作的最终完成 (或失败)及其结果值。对于新手来说,这听起来可能有点太复杂了。

国外一位大什么对Promises的解释如下:“想象一下你是个孩子。你老妈向你保证,她下周会给你买一部新手机。”

你要到下周才能知道你是否能获取那部手机。你老妈要么真的给你买了一个全新的手机,要么因为不开心就不给你买。

这个就是一个Promise。一个Promise有三个状态。分别是:

  1. Pending:你不知道你是否能得到那部手机
  2. Fulfilled:老妈高兴了,给你买了
  3. Rejected:老娘不开森了,不给你买了

这个是我目前听到,最快能理解 Promise 事例。

如果你还没有开始学习 Promise ,建议你这样做。

Promise包含几种非常有用的内置方法。今天我们主要介绍这两种方法。

  • Promise.race()-与 ES6 一起发布
  • Promise.any() -仍处于第4阶段的提案中

Promise.race()

Promise.race()方法最初是在 ES6 中引入 Promise 时发布的,这个方法需要一个iterable作为参数。

Promise.race(iterable) 方法返回一个 promise,一旦迭代器中的某个promise解决或拒绝,返回的 promise 就会解决或拒绝。

与Promise.any()方法不同,Promise.race()方法主要关注 Promise 是否已解决,而不管其被解决还是被拒绝。

语法

  1. Promise.race(iterable) 

参数

iterable — 可迭代对象,类似 Array。iterable 对象实现Symbol.iterator方法。

返回值

一个待定的 Promise 只要给定的迭代中的一个promise解决或拒绝,就采用第一个promise的值作为它的值,从而异步地解析或拒绝(一旦堆栈为空)。

注意

因为参数接受iterable,所以我们可以传递一些值,比如基本值,甚至数组中的对象。在这种情况下,race方法将返回传递的第一个非 promise 对象。这主要是因为方法的行为是在值可用时(当 promise 满足时)立即返回值。

此外,如果在iterable中传递了已经解决的Promise,则Promise.race()方法将解析为该值的第一个。如果传递了一个空的Iterable,则race方法将永远处于待处理状态。

事例

  1. const promise1 = new Promise((resolve, reject) => { 
  2.     setTimeout(resolve, 500, 'promise 1 resolved'); 
  3. }); 
  4.  
  5. const promise2 = new Promise((resolve, reject) => { 
  6.     setTimeout(reject, 100, 'promise 2 rejected'); 
  7. }); 
  8.  
  9. const promise3 = new Promise((resolve, reject) => { 
  10.     setTimeout(resolve, 200, 'promise 3 resolved'
  11. }); 
  12.  
  13. (async () => { 
  14.     try { 
  15.         let result = await Promise.race([promise1, promise2, promise3]); 
  16.         console.log(result); 
  17.     } catch (err) { 
  18.         console.error(err); 
  19.     } 
  20. })(); 
  21.   // 输出- "promise 2 rejected" 
  22.   // 尽管promise1和promise3可以解决,但promise2拒绝的速度比它们快。 
  23.   // 因此Promise.race方法将以promise2拒绝 

真实用例

现在,你可能想知道,我们在实战中何时 Promise.race() ?来看看。

在请求数据时,显示加载动画

使用加载动画开发中是非常常见。当数据响应时间较长时,如果没使用加载动画,看起来就像没有响应一样。但有时,响应太快了,我们需要加载动画时,增加一个非常小延迟时间,这样会让用户觉得我是在经常请求过来的。要实现这一点,只需使用Promise.race()方法,如下所示。

  1. function getUserInfo(user) { 
  2.   return new Promise((resolve, reject) => { 
  3.     // had it at 1500 to be more true-to-life, but 900 is better for testing 
  4.     setTimeout(() => resolve("user data!"), Math.floor(900*Math.random())); 
  5.   }); 
  6.  
  7. function showUserInfo(user) { 
  8.   return getUserInfo().then(info => { 
  9.     console.log("user info:", info); 
  10.     return true
  11.   }); 
  12.  
  13. function showSpinner() { 
  14.   console.log("please wait..."
  15.  
  16. function timeout(delay, result) { 
  17.   return new Promise(resolve => { 
  18.     setTimeout(() => resolve(result), delay); 
  19.   }); 
  20. Promise.race([showUserInfo(), timeout(300)]).then(displayed => { 
  21.   if (!displayed) showSpinner(); 
  22. }); 

**取消的 Promise **

有些情况下,我们需要取消 Promise,这时也可以借助 Promise.race() 方法:

  1. function timeout(delay) { 
  2.   let cancel; 
  3.   const wait = new Promise(resolve => { 
  4.     const timer = setTimeout(() => resolve(false), delay); 
  5.     cancel = () => { 
  6.       clearTimeout(timer); 
  7.       resolve(true); 
  8.     }; 
  9.   }); 
  10.   wait.cancel = cancel; 
  11.   return wait; 
  12.  
  13.  
  14. function doWork() { 
  15.   const workFactor = Math.floor(600*Math.random()); 
  16.   const work = timeout(workFactor); 
  17.    
  18.   const result = work.then(canceled => { 
  19.     if (canceled) 
  20.       console.log('Work canceled'); 
  21.     else 
  22.       console.log('Work done in', workFactor, 'ms'); 
  23.     return !canceled; 
  24.   }); 
  25.   result.cancel = work.cancel; 
  26.   return result; 
  27.  
  28. function attemptWork() { 
  29.   const work = doWork(); 
  30.   return Promise.race([work, timeout(300)]) 
  31.     .then(done => { 
  32.       if (!done) 
  33.         work.cancel(); 
  34.       return (done ? 'Work complete!' : 'I gave up'); 
  35.   }); 
  36.  
  37. attemptWork().then(console.log); 

批处理请求,用于长时间执行

Chris Jensen 有一个有趣的race()方法用例。他曾使用Promise.race()方法批处理长时间运行的请求。这样一来,他们可以保持并行请求的数量固定。

  1. const _ = require('lodash'
  2.  
  3. async function batchRequests(options) { 
  4.     let query = { offset: 0, limit: options.limit }; 
  5.  
  6.     do { 
  7.         batch = await model.findAll(query); 
  8.         query.offset += options.limit; 
  9.  
  10.         if (batch.length) { 
  11.             const promise = doLongRequestForBatch(batch).then(() => { 
  12.                 // Once complete, pop this promise from our array 
  13.                 // so that we know we can add another batch in its place 
  14.                 _.remove(promises, p => p === promise); 
  15.             }); 
  16.             promises.push(promise); 
  17.  
  18.             // Once we hit our concurrency limit, wait for at least one promise to 
  19.             // resolve before continuing to batch off requests 
  20.             if (promises.length >= options.concurrentBatches) { 
  21.                 await Promise.race(promises); 
  22.             } 
  23.         } 
  24.     } while (batch.length); 
  25.  
  26.     // Wait for remaining batches to finish 
  27.     return Promise.all(promises); 
  28.  
  29. batchRequests({ limit: 100, concurrentBatches: 5 }); 

Promise.any()

Promise.any() 接收一个Promise可迭代对象,只要其中的一个 promise 成功,就返回那个已经成功的 promise 。如果可迭代对象中没有一个 promise 成功(即所有的 promises 都失败/拒绝),就返回一个失败的 promise 和AggregateError类型的实例,它是 Error 的一个子类,用于把单一的错误集合在一起。本质上,这个方法和Promise.all()是相反的。

注意!Promise.any() 方法依然是实验性的,尚未被所有的浏览器完全支持。它当前处于 TC39 第四阶段草案(Stage 4)

语法

  1. Promise.any(iterable); 

参数

iterable — 个可迭代的对象, 例如 Array。

返回值

  • 如果传入的参数是一个空的可迭代对象,则返回一个 已失败(already rejected) 状态的 Promise。
  • 如果传入的参数不包含任何 promise,则返回一个 **异步完成 (asynchronously resolved)**的 Promise。
  • 其他情况下都会返回一个处理中(pending) 的 Promise。只要传入的迭代对象中的任何一个 promise 变成成功(resolve)状态,或者其中的所有的 promises 都失败,那么返回的 promise 就会 异步地(当调用栈为空时) 变成成功/失败(resolved/reject)状态。

说明

这个方法用于返回第一个成功的 promise 。只要有一个 promise 成功此方法就会终止,它不会等待其他的 promise 全部完成。

不像 Promise.all() 会返回一组完成值那样(resolved values),我们只能得到一个成功值(假设至少有一个 promise 完成)。当我们只需要一个 promise 成功,而不关心是哪一个成功时此方法很有用的。

同时, 也不像 Promise.race() 总是返回第一个结果值(resolved/reject)那样,这个方法返回的是第一个 成功的 值。这个方法将会忽略掉所有被拒绝的 promise,直到第一个 promise 成功。

事例

  1. const promise1 = new Promise((resolve, reject) => { 
  2.   setTimeout(reject, 100, 'promise 1 rejected'); 
  3. }); 
  4.  
  5. const promise2 = new Promise((resolve, reject) => { 
  6.   setTimeout(resolve, 400, 'promise 2 resolved at 400 ms'); 
  7. }); 
  8.  
  9. const promise3 = new Promise((resolve, reject) => { 
  10.   setTimeout(resolve, 700, 'promise 3 resolved at 800 ms'); 
  11. }); 
  12.  
  13. (async () => { 
  14.   try { 
  15.     let value = await Promise.any([promise1, promise2, promise3]); 
  16.     console.log(value); 
  17.   } catch (error) { 
  18.     console.log(error); 
  19.   } 
  20. })(); 
  21.  
  22. //Output - "promise 2 resolved at 400 ms" 

从上面代码注意到Promise.any()主要关注解析的值。它会忽略在100毫秒时拒绝的promise1,并考虑在400毫秒后解析的promise2的值。

真实用例

从最快的服务器检索资源

假设访问我们网站的用户可能来自全球各地。如果我们的服务器基于单个位置,那么响应时间将根据每个用户的位置而不同。但是如果我们有多个服务器,可以使用能够产生最快响应的服务器。在这种情况下,可以使用Promise.any()方法从最快的服务器接收响应。

我是小智,我们下期再见!

作者:Mahdhi Rezvi 译者:前端小智 来源: medium

原文:https://blog.bitsrc.io/introduction-to-promise-race-and-promise-any-with-real-life-examples-9d8d1b9f8ec9

本文转载自微信公众号「大迁世界」,可以通过以下二维码关注。转载本文请联系大迁世界公众号。

 

责任编辑:武晓燕 来源: 大迁世界
相关推荐

2021-04-28 08:21:21

Promise.any服务器场景

2023-09-15 15:31:23

异步编程Promise

2020-09-18 09:02:20

JavaScript

2023-01-12 11:23:11

Promise异步编程

2021-03-15 08:15:42

ES2021语言开发

2022-09-28 12:23:36

Promise代码

2020-07-29 17:35:08

Promise源码前端

2021-04-29 08:28:24

架构参数传递

2023-10-08 10:21:11

JavaScriptAsync

2021-04-27 08:31:37

Promisereject信息

2018-03-13 16:04:45

Promise执行顺序

2021-03-09 07:37:42

技术Promise测试

2023-10-04 07:25:59

JavaScriptpromises

2021-09-02 12:07:48

Swift 监听系统Promise

2019-12-09 15:20:09

JavascriptPromise前端

2015-07-23 11:59:27

JavascriptPromise

2017-05-11 20:20:59

JavascriptPromiseWeb

2020-08-31 08:11:01

V8 8.5Promise前端

2016-11-01 19:22:36

Javascript前端Promise

2022-01-21 08:50:15

Promise任务队列前端
点赞
收藏

51CTO技术栈公众号