Skip to content

Commit 6745c59

Browse files
committed
explainer: Lack of eager rejection handling
Closes #16.
1 parent 299ac6b commit 6745c59

File tree

1 file changed

+91
-4
lines changed

1 file changed

+91
-4
lines changed

Diff for: README.md

+91-4
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ ECMAScript Stage-2 Proposal. J. S. Choi, 2021.
99
[specification]: https://tc39.es/proposal-array-from-async/
1010
[core-js]: https://github.com/zloirock/core-js#arrayfromasync
1111
[array-from-async]: https://www.npmjs.com/package/array-from-async
12+
[§ Errors]: #errors
1213

1314
## Why an Array.fromAsync method
1415
Since its standardization in JavaScript, **[Array.from][]** has become one of
@@ -102,6 +103,12 @@ for await (const v of genPromises(4)) {
102103
const arr = await Array.fromAsync(genPromises(4));
103104
```
104105

106+
Also like `for await`, when given a sync-but-not-async iterable input, then
107+
Array.fromAsync **will not handle** any yielded promises that **reject**.
108+
This is because `Array.fromAsync` **lazily** iterates over its input, so it
109+
cannot attach any handlers to its input’s promise values. For more information,
110+
see [§ Errors][].
111+
105112
### Non-iterable array-like inputs
106113
Array.fromAsync’s valid inputs are a superset of Array.from’s valid inputs. This
107114
includes non-iterable array-likes: objects that have a length property as well
@@ -134,6 +141,11 @@ for await (const v of Array.from(arrLike)) {
134141
const arr = await Array.fromAsync(arrLike);
135142
```
136143

144+
As with sync iterables, when given a non-iterable input, then Array.fromAsync
145+
**will not handle** any yielded promises that **reject**. This is because
146+
`Array.fromAsync` **lazily** iterates over its input, so it cannot attach any
147+
handlers to its input’s promise values. For more information, see [§ Errors][].
148+
137149
### Generic factory method
138150
Array.fromAsync is a generic factory method. It does not require that its this
139151
receiver be the Array constructor. fromAsync can be transferred to or inherited
@@ -235,20 +247,95 @@ rejected, then Array.fromAsync’s returned promise will reject with that error.
235247

236248
```js
237249
const err = new Error;
238-
function * genRejection () { yield Promise.reject(err); }
250+
const rejection = Promise.reject(err);
251+
function * genZeroThenRejection () {
252+
yield 0;
253+
yield rejection;
254+
}
239255

240-
// This creates a promise that will reject with `err`.
241-
Array.fromAsync(genRejection());
256+
// This creates a promise that will reject with `err`. However, `rejection`
257+
// itself will not be handled by Array.fromAsync.
258+
Array.fromAsync(genZeroThenRejection());
242259
```
243260

244261
```js
245262
const err = new Error;
246-
const arrLikeWithRejection = { length: 1, 0: Promise.reject(err) };
263+
const arrLikeWithRejection = {
264+
length: 2,
265+
0: 0,
266+
1: Promise.reject(err),
267+
};
247268

248269
// This creates a promise that will reject with `err`.
249270
Array.fromAsync(arrLikeWithRejection);
250271
```
251272

273+
However, like `for await`, in this case Array.fromAsync **will not handle** any
274+
yielded rejecting promises. This is because `Array.fromAsync` **lazily**
275+
iterates over its input, so it cannot attach any handlers to its input’s promise
276+
values.
277+
278+
The creator of the rejecting promise is expected to synchronously attach a
279+
rejection handler when the promise is created, as usual:
280+
281+
```js
282+
const err = new Error;
283+
// The creator of the rejecting promise attaches a rejection handler.
284+
const rejection = Promise.reject(err).catch(console.error);
285+
function * genZeroThenRejection () {
286+
yield 0;
287+
yield rejection;
288+
}
289+
290+
// This still creates a promise that will reject with `err`. `err` will also
291+
// separately be printed to the console due to the rejection handler.
292+
Array.fromAsync(genZeroThenRejection());
293+
```
294+
295+
```js
296+
const err = new Error;
297+
const arrLikeWithRejection = {
298+
length: 2,
299+
0: 0,
300+
1: Promise.reject(err),
301+
};
302+
303+
// This still creates a promise that will reject with `err`. `err` will also
304+
// separately be printed to the console due to the rejection handler.
305+
Array.fromAsync(arrLikeWithRejection);
306+
```
307+
308+
Alternatively, the user of the promises can switch from Array.fromAsync to
309+
Promise.all. Promise.all would change the control flow from lazy sync iteration
310+
(with sequential awaiting) to eager sync iteration (with parallel awaiting),
311+
allowing the handling of any rejection in the input.
312+
313+
```js
314+
const err = new Error;
315+
const rejection = Promise.reject(err);
316+
function * genZeroThenRejection () {
317+
yield 0;
318+
yield rejection;
319+
}
320+
321+
// Creates a promise that will reject with `err`. Unlike Array.fromAsync,
322+
// Promise.all will handle the `rejection`.
323+
Promise.all(genZeroThenRejection());
324+
```
325+
326+
```js
327+
const err = new Error;
328+
const arrLikeWithRejection = {
329+
length: 2,
330+
0: 0,
331+
1: Promise.reject(err),
332+
};
333+
334+
// Creates a promise that will reject with `err`. Unlike Array.fromAsync,
335+
// Promise.all will handle the `rejection`.
336+
Promise.all(Array.from(arrLikeWithRejection));
337+
```
338+
252339
If Array.fromAsync’s input has at least one value, and Array.fromAsync’s mapping
253340
callback throws an error when given any of those values, then Array.fromAsync’s
254341
returned promise will reject with the first such error.

0 commit comments

Comments
 (0)