-
Notifications
You must be signed in to change notification settings - Fork 23.2k
Expand file tree
/
Copy pathindex.md
More file actions
337 lines (267 loc) · 11.4 KB
/
Copy pathindex.md
File metadata and controls
337 lines (267 loc) · 11.4 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
---
title: Promise.all()
short-title: all()
slug: Web/JavaScript/Reference/Global_Objects/Promise/all
page-type: javascript-static-method
browser-compat: javascript.builtins.Promise.all
sidebar: jsref
---
The **`Promise.all()`** static method takes an iterable of promises as input and returns a single {{jsxref("Promise")}}. This returned promise fulfills when all of the input's promises fulfill (including when an empty iterable is passed), with an array of the fulfillment values. It rejects when any of the input's promises rejects, with this first rejection reason.
{{InteractiveExample("JavaScript Demo: Promise.all()")}}
```js interactive-example
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);
});
// Expected output: Array [3, 42, "foo"]
```
## Syntax
```js-nolint
Promise.all(iterable)
```
### Parameters
- `iterable`
- : An [iterable](/en-US/docs/Web/JavaScript/Reference/Iteration_protocols#the_iterable_protocol) (such as an {{jsxref("Array")}}) of promises. These values are [awaited](/en-US/docs/Web/JavaScript/Reference/Operators/await), so other [thenables](/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise#thenables) are also resolved, while non-thenables are returned as-is.
### Return value
A {{jsxref("Promise")}} that is:
- **Already fulfilled**, if the `iterable` passed is empty.
- **Asynchronously fulfilled**, when all the promises in the given `iterable` fulfill. The fulfillment value is an array of fulfillment values, in the order of the promises passed, regardless of completion order. If the `iterable` passed is non-empty but contains no pending promises, the returned promise is still asynchronously (instead of synchronously) fulfilled.
- **Asynchronously rejected**, when any of the promises in the given `iterable` rejects. The rejection reason is the rejection reason of the first promise that was rejected.
## Description
The `Promise.all()` method is one of the [promise concurrency](/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise#promise_concurrency) methods. It can be useful for aggregating the results of multiple promises. It is typically used when there are multiple related asynchronous tasks that the overall code relies on to work successfully — all of whom we want to fulfill before the code execution continues.
`Promise.all()` will reject immediately upon **any** of the input promises rejecting. In comparison, the promise returned by {{jsxref("Promise.allSettled()")}} will wait for all input promises to complete, regardless of whether or not one rejects. Use `allSettled()` if you need the final result of every promise in the input iterable.
Like other promise combinators, `Promise.all()` immediately marks all promises as "handled" when it is called (by calling their `.then()` methods). Subsequent rejections after the first rejection will be ignored, and will not trigger any `unhandledrejection` events.
## Examples
### Using Promise.all()
`Promise.all` waits for all fulfillments (or the first rejection).
```js
const p1 = Promise.resolve(3);
const p2 = 1337;
const p3 = new Promise((resolve, reject) => {
setTimeout(() => {
resolve("foo");
}, 100);
});
Promise.all([p1, p2, p3]).then((values) => {
console.log(values); // [3, 1337, "foo"]
});
```
If the `iterable` contains non-promise values, they will be ignored, but still counted in the returned promise array value (if the promise is fulfilled):
```js
// All values are non-promises, so the returned promise gets fulfilled
const p = Promise.all([1, 2, 3]);
// The only input promise is already fulfilled,
// so the returned promise gets fulfilled
const p2 = Promise.all([1, 2, 3, Promise.resolve(444)]);
// One (and the only) input promise is rejected,
// so the returned promise gets rejected
const p3 = Promise.all([1, 2, 3, Promise.reject(new Error("bad"))]);
// Using setTimeout, we can execute code after the queue is empty
setTimeout(() => {
console.log(p);
console.log(p2);
console.log(p3);
});
// Logs:
// Promise { <state>: "fulfilled", <value>: Array[3] }
// Promise { <state>: "fulfilled", <value>: Array[4] }
// Promise { <state>: "rejected", <reason>: Error: bad }
```
### Destructuring the result
You will find [destructuring](/en-US/docs/Web/JavaScript/Reference/Operators/Destructuring) very useful if you are batching together a known number of tasks.
```js
// With then()
Promise.all([p1, p2, p3]).then(([a, b, c]) => {
console.log(a, b, c); // 3 1337 "foo"
});
// With await
const [a, b, c] = await Promise.all([p1, p2, p3]);
```
Be careful: if the original promises and the result variables' order don't match, you may run into subtle bugs. The {{jsxref("Promise.allKeyed()")}} method solves exactly this problem.
### Asynchronicity or synchronicity of Promise.all
This following example demonstrates the asynchronicity of `Promise.all` when a non-empty `iterable` is passed:
```js
// Passing an array of promises that are already resolved,
// to trigger Promise.all as soon as possible
const resolvedPromisesArray = [Promise.resolve(33), Promise.resolve(44)];
const p = Promise.all(resolvedPromisesArray);
// Immediately logging the value of p
console.log(p);
// Using setTimeout, we can execute code after the queue is empty
setTimeout(() => {
console.log("the queue is now empty");
console.log(p);
});
// Logs, in order:
// Promise { <state>: "pending" }
// the queue is now empty
// Promise { <state>: "fulfilled", <value>: Array[2] }
```
The same thing happens if `Promise.all` rejects:
```js
const mixedPromisesArray = [
Promise.resolve(33),
Promise.reject(new Error("bad")),
];
const p = Promise.all(mixedPromisesArray);
console.log(p);
setTimeout(() => {
console.log("the queue is now empty");
console.log(p);
});
// Logs:
// Promise { <state>: "pending" }
// the queue is now empty
// Promise { <state>: "rejected", <reason>: Error: bad }
```
`Promise.all` resolves synchronously if and only if the `iterable` passed is empty:
```js
const p = Promise.all([]); // Will be immediately resolved
const p2 = Promise.all([1337, "hi"]); // Non-promise values are ignored, but the evaluation is done asynchronously
console.log(p);
console.log(p2);
setTimeout(() => {
console.log("the queue is now empty");
console.log(p2);
});
// Logs:
// Promise { <state>: "fulfilled", <value>: Array[0] }
// Promise { <state>: "pending" }
// the queue is now empty
// Promise { <state>: "fulfilled", <value>: Array[2] }
```
### Using Promise.all() with async functions
Within [async functions](/en-US/docs/Web/JavaScript/Reference/Statements/async_function), it's very common to "over-await" your code. For example, given the following functions:
```js
function promptForDishChoice() {
return new Promise((resolve, reject) => {
const dialog = document.createElement("dialog");
dialog.innerHTML = `
<form method="dialog">
<p>What would you like to eat?</p>
<select>
<option value="pizza">Pizza</option>
<option value="pasta">Pasta</option>
<option value="salad">Salad</option>
</select>
<menu>
<li><button value="cancel">Cancel</button></li>
<li><button type="submit" value="ok">OK</button></li>
</menu>
</form>
`;
dialog.addEventListener("close", () => {
if (dialog.returnValue === "ok") {
resolve(dialog.querySelector("select").value);
} else {
reject(new Error("User canceled dialog"));
}
});
document.body.appendChild(dialog);
dialog.showModal();
});
}
async function fetchPrices() {
const response = await fetch("/prices");
return await response.json();
}
```
You may write a function like this:
```js example-bad
async function getPrice() {
const choice = await promptForDishChoice();
const prices = await fetchPrices();
return prices[choice];
}
```
However, note that the execution of `promptForDishChoice` and `fetchPrices` don't depend on the result of each other. While the user is choosing their dish, it's fine for the prices to be fetched in the background, but in the code above, the [`await`](/en-US/docs/Web/JavaScript/Reference/Operators/await) operator causes the async function to pause until the choice is made, and then again until the prices are fetched. We can use `Promise.all` to run them concurrently, so that the user doesn't have to wait for the prices to be fetched before the result is given:
```js example-good
async function getPrice() {
const [choice, prices] = await Promise.all([
promptForDishChoice(),
fetchPrices(),
]);
return prices[choice];
}
```
`Promise.all` is the best choice of [concurrency method](/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise#promise_concurrency) here, because error handling is intuitive — if any of the promises reject, the result is no longer available, so the whole `await` expression throws.
`Promise.all` accepts an iterable of promises, so if you are using it to run several async functions concurrently, you need to call the async functions and use the returned promises. Directly passing the functions to `Promise.all` does not work, since they are not promises.
```js example-bad
async function getPrice() {
const [choice, prices] = await Promise.all([
promptForDishChoice,
fetchPrices,
]);
// `choice` and `prices` are still the original async functions;
// Promise.all() does nothing to non-promises
}
```
Note that the following, despite other undesirable aspects (like creating more variables and bug-prone error handling), also achieves concurrency:
```js
async function getPrice() {
// Fire all async operations upfront
const choicePromise = promptForDishChoice();
const pricesPromise = fetchPrices();
// Wait for each promise (all in-progress async operations keep running)
const choice = await choicePromise;
const prices = await pricesPromise;
}
```
### Promise.all fail-fast behavior
`Promise.all` is rejected if any of the elements are rejected. For example, if you pass in four promises that resolve after a timeout and one promise that rejects immediately, then `Promise.all` will reject immediately.
```js
const p1 = new Promise((resolve, reject) => {
setTimeout(() => resolve("one"), 1000);
});
const p2 = new Promise((resolve, reject) => {
setTimeout(() => resolve("two"), 2000);
});
const p3 = new Promise((resolve, reject) => {
setTimeout(() => resolve("three"), 3000);
});
const p4 = new Promise((resolve, reject) => {
setTimeout(() => resolve("four"), 4000);
});
const p5 = new Promise((resolve, reject) => {
reject(new Error("reject"));
});
// Using .catch:
Promise.all([p1, p2, p3, p4, p5])
.then((values) => {
console.log(values);
})
.catch((error) => {
console.error(error.message);
});
// Logs:
// "reject"
```
It is possible to change this behavior by handling possible rejections:
```js
const p1 = new Promise((resolve, reject) => {
setTimeout(() => resolve("p1_delayed_resolution"), 1000);
});
const p2 = new Promise((resolve, reject) => {
reject(new Error("p2_immediate_rejection"));
});
Promise.all([p1.catch((error) => error), p2.catch((error) => error)]).then(
(values) => {
console.log(values[0]); // "p1_delayed_resolution"
console.error(values[1]); // "Error: p2_immediate_rejection"
},
);
```
## Specifications
{{Specifications}}
## Browser compatibility
{{Compat}}
## See also
- {{jsxref("Promise")}}
- {{jsxref("Promise.allKeyed()")}}
- {{jsxref("Promise.allSettled()")}}
- {{jsxref("Promise.any()")}}
- {{jsxref("Promise.race()")}}