JavaScript:如何使用Promise.all()函数

68 阅读3分钟

如何使用Promise.all()

简单地说,承诺是一个占位符,用来表示一个稍后会出现的值。

诺言在处理异步操作时非常有用。

JavaScript提供了一个辅助函数Promise.all(promisesArrayOrIterable) ,可以同时并行处理多个承诺,并在一个聚合数组中获得结果。让我们看看它是如何工作的。

1.Promise.all()

Promise.all() 是一个内置的辅助函数,它接受一个承诺数组(或一般是一个可迭代的)。该函数返回一个承诺。

const allPromise = Promise.all([promise1, promise2, ...]);

然后,你可以使用then-able语法提取承诺的解析值

allPromise.then(values => {
  values; // [valueOfPromise1, valueOfPromise2, ...]
}).catch(error => {
  error;  // rejectReason of any first rejected promise
});

async/await 语法

try {
  const values = await allPromise;
  values; // [valueOfPromise1, valueOfPromise2, ...]
} catch (error) {
  error;  // rejectReason of any first rejected promise
}

有趣的部分是在由Promise.all() 返回的承诺被解析或拒绝的方式。

如果所有的承诺都被成功地解决了,那么allPromise ,用一个包含各个承诺的实现值的数组来实现。数组中承诺的顺序的确很重要--你将得到按该顺序实现的值。

Promise.all() - all fullfilled

但是,如果至少有一个承诺被拒绝,那么allPromise 就会以同样的理由立即拒绝(不等待其他承诺的解决)。

Promise.all() - one rejects

让我们通过几个例子来看看如何使用Promise.all() 来同时执行多个异步操作。

2.例子:所有的承诺都实现了

为了研究Promise.all() 如何工作,我将使用2个助手--resolveTimeout(value, delay)rejectTimeout(reason, delay)

function resolveTimeout(value, delay) {
  return new Promise(
    resolve => setTimeout(() => resolve(value), delay)
  );
}
function rejectTimeout(reason, delay) {
  return new Promise(
    (r, reject) => setTimeout(() => reject(reason), delay)
  );
}

resolveTimeout(value, delay) 返回一个承诺,该承诺在通过 value时间后与 delay 一起实现。

delay 在另一边,rejectTimeout(reason, delay) 返回一个承诺,该承诺在通过reason (通常是一个错误)的时间后被拒绝。

例如,让我们在同一时间访问当地杂货店的蔬菜和水果清单。访问每个列表是一个异步操作。

const allPromise = Promise.all([
  resolveTimeout(['potatoes', 'tomatoes'], 1000),
  resolveTimeout(['oranges', 'apples'], 1000)
]);
// wait...
const lists = await allPromise;
// after 1 second
console.log(lists); 
// [['potatoes', 'tomatoes'], ['oranges', 'apples']]

const allPromise = Promise.all([...]) 返回一个新的承诺 :allPromise

然后,语句const lists = await allPromise 等待1秒钟,直到allPromise 得到满足,其中包含第一个和第二个承诺的满足值的数组。

最后,lists 包含聚合的结果:[['potatoes', 'tomatoes'], ['oranges', 'apples']]

承诺数组的顺序直接影响到结果的顺序

蔬菜承诺是第一项,而水果承诺是输入数组中的第二项:Promise.all([vegetablesPromise, fruitsPromise]) 。结果数组包含相同顺序的值--第一个蔬菜列表和第二个水果列表。

3.例子:一个承诺被拒绝

现在想象一下这样的情况:杂货店里的水果已经用完了。在这种情况下,让我们用一个错误来拒绝水果的承诺new Error('Out of fruits!')

const allPromise = Promise.all([
  resolveTimeout(['potatoes', 'tomatoes'], 1000),
  rejectTimeout(new Error('Out of fruits!'), 1000)
]);
try {
  // wait...
  const lists = await allPromise;
} catch (error) {
  // after 1 second
  console.log(error.message); // 'Out of fruits!'
}

在这种情况下,allPromise = Promise.all([...]) ,像往常一样,返回一个承诺。

然而,在经过1秒钟后,第二个承诺(水果)被拒绝,并出现错误new Error('Out of fruits!') 。这使得allPromise ,同样的new Error('Out of fruits!') ,立即拒绝。

即使蔬菜承诺已经实现,Promise.all() 也不考虑它。

Promise.all([...]) 的这种行为被命名为失败-快速。如果承诺数组中至少有一个承诺被拒绝,那么由allPromise = Promise.all([...]) 返回的承诺也会被拒绝--理由相同。

4.4.结论

Promise.all([...]) 是一个有用的辅助函数,它可以让你使用故障快速策略并行地执行异步操作,并将结果聚集到一个数组中。