An asynchronous array conversion can be waiting for the first mapper result while the source has not yet been asked for its second item. That is different from eagerly starting work for every input and later waiting for an aggregate result. Array.fromAsync has a sequential consumption contract, and its precise waiting behaviour depends on whether the selected source is async iterable, sync iterable or only array-like.
The short answer is to trace the next-item request, the obtained value, the mapper call and the awaited mapper result before moving to another item. Keep already-started work separate from work that begins when an iterator is pulled. This guide uses original finite producers and controlled promise gates. It makes no network request and does not use elapsed-time guesses as evidence of ordering.
This specialist guide belongs to the Websites root. It also connects to The Gold Standard of Education | Learning That Transfers into Life and the How X Works hub. The worked cases are original teaching examples; their numbers are stipulated so that the reasoning can be checked.
eduKateSG · Websites
Find your next learning step
Trace Array.fromAsync pulls while mapping results are awaited. Compare source protocols, promise-valued items, already-started work and bounded cleanup with controlled gates.
ROUTE 1 · CHAPTERS 1–4
Identify the promise and source
Choose the protocol, observe a finite producer and hold the first mapper behind a gate.
ROUTE 2 · CHAPTERS 5–7
Separate waiting from work creation
Trace sequential consumption and compare already-started work with deliberate eager aggregation.
ROUTE 3 · CHAPTERS 8–10
Inspect promise-valued items
Compare synchronous input, a raw async iterator and an async generator.
ROUTE 4 · CHAPTERS 11–13
Follow reads and failure
Inspect array-like positions, missing values and the controlled rejection path.
ROUTE 5 · CHAPTERS 14–18
Test lifetime and practical transfer
Keep pending, cancellation, retained references and finite materialisation distinct.
Chapter contents
Identify the promise and source · Chapters 1–4
1. Separate the returned promise from the completed array
2. Identify the selected source protocol first
Separate waiting from work creation · Chapters 5–7
5. Await each mapper result before requesting the next item
6. Compare lazy creation with already-started promises
7. Compare deliberate eager aggregation without mixing source kinds
Inspect promise-valued items · Chapters 8–10
8. Trace sync-iterable promise values before mapping
9. Do not automatically unwrap a raw async iterator's value field
10. Keep async generator yield behaviour separate from a raw iterator
Follow reads and failure · Chapters 11–13
11. Follow array-like reads and awaited mapping in order
Test lifetime and practical transfer · Chapters 14–18
14. Keep a pending wait separate from a timeout or abort policy
15. Remember that the completed collection is shallow and finite
16. Test event boundaries with gates rather than elapsed-time folklore
CHAPTER 1 OF 18 · Identify the promise and source
1. Separate the returned promise from the completed array
Array.fromAsync returns a promise for the resulting array. The existence of that promise does not mean that every item has been obtained or that every mapper result has fulfilled. A caller requiring the completed array must observe the promise's outcome through an appropriate await or promise chain. Keep the operation's returned value and its eventual fulfilment value as separate objects in the explanation.
That distinction does not mean nothing can begin before the caller receives the promise. Source-method access and initial processing follow the actual asynchronous algorithm. A test should record concrete events rather than assume a completely inactive preparation phase. The central guarantee we will examine concerns the sequence of pulls and waits during consumption, not a vague statement that all activity starts later.
For a friendly baseline, convert the finite array two, four and six with a mapper that doubles each value. Awaiting the returned promise gives four, eight and twelve. The result is useful but insufficient to distinguish sequential consumption from an eager implementation: both could produce those values when everything is already available and the mapper has no effects.
A stronger fixture introduces a controlled pending mapper result and a producer that records each next request. That lets us ask whether the second request has occurred before the first mapping finishes. We can answer by observing a gate and an event log, without relying on a particular processor speed, timer delay or developer-console presentation of an object that may change later.
CHAPTER 2 OF 18 · Identify the promise and source
2. Identify the selected source protocol first
Array.fromAsync can consume an async iterable, a sync iterable or an array-like object. The selection order matters. When the async iteration method is available under the required contract, that path is selected. Otherwise a suitable synchronous iterator can be used, and a non-iterable array-like source has its own indexed path. A visible length property does not automatically determine the source sequence.
Use an invented object with indexed values two and four, length two, a sync iterator yielding eight and an async iterator yielding nine. Under the ordinary method-selection rules, the async iterable supplies the sequence. Removing that method in a fresh fixture exposes the sync iterator's sequence. Removing both suitable iteration methods exposes the array-like fields. These are three different input contracts, not competing descriptions of one guaranteed two-item array.
A present but invalid iteration method can cause an error rather than a silent fallback to whichever source interpretation would be most convenient. Keep malformed-protocol cases separate from cases in which a method is simply absent. The exact lookup and callability requirements belong to the selected API, so do not predict behaviour only from a source's informal class name.
The practical first record names the source path and how a fresh iterator is obtained. Reusing an exhausted generator in a second test would change the experiment before the waiting policy is even reached. Use a new finite source for each comparison, and retain its selected sequence beside the expected mapper inputs. This prevents a protocol-selection difference from being misdiagnosed as an asynchronous scheduling problem.
CHAPTER 3 OF 18 · Identify the promise and source
3. Build a finite producer with observable pulls
Create a producer that offers two primitive values, two and four, and then reports completion. Each next call records pull zero, pull one or the final completion request. The producer should not start external work merely because the object was created. In this deliberately lazy fixture, requesting an item is the event that makes the next value available.
A custom async iterator can return a promise for each iterator-result object. The result object states whether the sequence is done and, when relevant, supplies a value. Those are separate layers: awaiting the next method's result is not automatically the same operation as awaiting a promise stored inside that result's value field. We will change that inner field deliberately later.
For the first fixture, use primitive values so no nested-promise distinction complicates the pull sequence. Keep the producer finite and the pull counter bounded. An unending producer would turn an array-collection example into a resource problem without teaching the intended boundary more clearly. Two values and a final done result are enough to expose whether the consumer asks for the next item too early.
The evidence log should contain immutable labels or primitive records. Inspecting a live iterator object after completion may show only its final counter. That final state cannot establish whether pull one happened before or after map zero finished. A sequence of events preserves the missing relationship and makes the test reviewable by another reader without depending on a screenshot or an unexplained timing measurement.
CHAPTER 4 OF 18 · Identify the promise and source
4. Hold the first mapper result behind a controlled gate
Let the first mapper call record map zero and wait on a promise whose resolver the test controls. Also create a separate started signal that resolves when that mapper has actually begun. The test awaits the started signal, then inspects the pull log before releasing the gate. At that point, the first mapper is pending and the producer has not been pulled for its second item.
let releaseFirst, markStarted;
const gate = new Promise(resolve => { releaseFirst = resolve; });
const started = new Promise(resolve => { markStarted = resolve; });
const events = [];
let index = 0;
const source = {
[Symbol.asyncIterator]() { return this; },
async next() {
events.push(`pull ${index}`);
return index < 2 ? { value: [2, 4][index++], done: false } : { done: true };
}
};
const pending = Array.fromAsync(source, async (value, i) => {
events.push(`map ${i}`);
if (i === 0) { markStarted(); await gate; }
events.push(`mapped ${i}`);
return value * 2;
});
await started; // Run this fixture in an async context.
// events is ["pull 0", "map 0"] here.
releaseFirst();
const result = await pending; // [4, 8]
The started signal makes the observation deterministic. Merely waiting one arbitrary millisecond and hoping that the first callback has begun would test machine timing as well as the language contract. The controlled gate instead establishes the exact state we need: the mapper has been invoked but its awaited result is not yet available.
After release, the first mapping completes, the consumer continues and the next item can be requested. The full log records the final completion pull as well as the two value pulls. Counting next calls without distinguishing a done result from a supplied item can create an off-by-one explanation. The final request establishes exhaustion; it is not a third mapped value.
CHAPTER 5 OF 18 · Separate waiting from work creation
5. Await each mapper result before requesting the next item
The central fixture's ordering is not limited to an async mapper written with the async keyword. A mapper can return any appropriate value, including a pending promise, and the operation awaits its result. A plain returned number is also handled through the defined asynchronous process. The relevant question is what value the call returns, not which spelling makes the function look asynchronous.
For a changed example, let an ordinary mapper return the controlled gate promise transformed into a numeric result. While that promise remains pending for the first item, no next-item request occurs. Once it fulfils, the mapped value is stored and consumption continues. This demonstrates the awaited-output boundary without relying on an async function declaration.
The mapper's arguments are the obtained element and its index. It is not given a third argument containing the fully completed output array. That array is still under construction. A callback borrowed from Array.prototype.map may rely on an argument that this interface does not supply, so adapt the callback to its actual contract rather than adding await to an incompatible function and expecting the arguments to change.
Sequential waiting can be useful when the producer's next step should follow completion of the current transformation. It is not universally the most efficient scheduling choice. If the task requires several independent operations in flight, design that policy explicitly with its own capacity and failure handling. The method's defined sequence answers one question; it does not decide every application's throughput requirement.
CHAPTER 6 OF 18 · Separate waiting from work creation
6. Compare lazy creation with already-started promises
A producer can create an operation only when next is called. An array can instead hold promises for operations that were started before Array.fromAsync was invoked. Sequential consumption of the latter array does not travel backwards in time and postpone the work those promises already represent. Waiting policy and start policy are different dimensions.
Imagine three locally controlled tasks A, B and C. If all three promises were created and their work started first, they can progress independently while the consumer awaits A. Array.fromAsync may later observe B and C in sequence, but that observation order does not prove that only A's underlying work was active. The source representation already determined that the work existed.
In a deliberately lazy generator, constructing each next promise occurs only when the consumer advances the iterator. Sequential pulling can then limit how many producer-created tasks have been started through that path. The conclusion remains conditional on the producer not starting additional hidden work elsewhere. A pull log is evidence about pulls, not a complete inventory of every external operation in the system.
For transfer, use the same controlled gates in two fresh fixtures: one generator creates each gate-backed task when pulled; another source is an array of three already-created promises. Record task creation separately from task settlement and consumer observation. The final array values may match while the creation histories differ. That is the practical reason to avoid describing the method simply as making all asynchronous work sequential.
CHAPTER 7 OF 18 · Separate waiting from work creation
7. Compare deliberate eager aggregation without mixing source kinds
Promise.all can aggregate a synchronous iterable of promises and retrieve its inputs eagerly under its own contract. A lazy synchronous generator that creates a task at each yield can therefore have all of its finite tasks created before any of them settles. Array.fromAsync consumption of that same kind of source waits through its sequential path before requesting the next yielded item.
This comparison needs compatible source types. Promise.all does not become an async-iterable consumer merely because its result is a promise. Passing a custom async-only iterable changes the input requirement rather than providing a fair comparison of scheduling over the same accepted sequence. Use a synchronous finite generator for the eager-versus-sequential task-creation exercise.
Neither aggregate rejection nor sequential waiting establishes cancellation of already-started work. The linked Promise.all guide owns that separate distinction. Here, the concrete observation is when the consumer retrieves the next input and when the producer creates the task associated with that retrieval. Keep cancellation claims outside the result unless the actual producer's supported stop mechanism was exercised and verified.
A useful report says which source was lazy, which tasks were created, which promises were awaited and which outcome was observed. It should not claim a universal elapsed-time speedup. Independent tasks with different resource limits can favour different designs. The original gate exercise proves ordering under controlled conditions; it does not benchmark a real server or certify that an eager strategy is suitable for every workload.
CHAPTER 8 OF 18 · Inspect promise-valued items
8. Trace sync-iterable promise values before mapping
For a synchronous iterable consumed through Array.fromAsync, yielded values are awaited as part of the selected path before the mapper receives them. A sync generator yielding Promise.resolve of three can therefore supply the number three to a mapper after the relevant wait. That is different from ordinary synchronous Array.from, which does not await a mapper's returned promises or automatically adopt this asynchronous consumption contract.
Use a controlled promise as the first yielded value and record when the mapper is invoked. Before that value fulfils, the mapper has not received a fulfilled primitive from it. After fulfilment with three, the mapper can record three and return six. The next item follows only after the mapped result is processed under the sequential contract.
This is a useful place to avoid a misleading log. Logging the promise object before fulfilment and later expanding it can make it look as though the number had been available all along. Record the mapper's actual primitive argument when the callback runs. That observation establishes what crossed the input boundary at that point, rather than merely showing the promise's eventual state.
For practical design, name whether a source supplies immediate values, promises for values or an async iteration protocol. These forms can lead to similar fulfilled arrays in a friendly example but differ when a value is pending, rejected or itself a promise nested inside another protocol object. The next chapter changes only that protocol layer so the distinction can be tested precisely.
CHAPTER 9 OF 18 · Inspect promise-valued items
9. Do not automatically unwrap a raw async iterator's value field
A custom async iterator's next method can fulfil with an iterator-result object whose value field is itself a promise. Under this async-iterable path, that field is passed as obtained rather than being automatically awaited before a mapper call. With no mapper, the resulting array can retain the promise value as an element. Awaiting the outer conversion promise does not recursively await every promise stored inside the fulfilled array.
Create a fresh finite raw async iterator that supplies one designated inner promise and then finishes. Without a mapper, assert that the resulting array's first element is that exact promise object. With an identity mapper, the mapper returns the promise it received, and the mapper-output await can adopt its fulfilment. Those two calls have different boundaries even though the mapper appears to return its input unchanged.
The distinction is not a recommendation to produce awkward nested promise values. It is a precise explanation of a valid controlled source. A consumer should follow the actual protocol instead of assuming that every layer of a promise-shaped structure is recursively flattened. The returned array is an ordinary container and can legitimately hold promise objects as values.
Use a new iterator instance for each comparison and observe the inner promise's failures deliberately in any rejection test. An exhausted first iterator would produce an empty second result for an unrelated reason. A clean fixture preserves the source difference being tested and makes it possible to say exactly whether the mapper received a primitive or a promise value.
| Source path | Mapper receives | Collection observation in this fixture |
|---|---|---|
| Synchronous iterable with promise value | Fulfilled item value | Fulfilled item value stored |
| Array-like position with promise value | Fulfilled position value | Fulfilled position value stored |
| Raw async iterator with promise in value field | That promise object | That promise object retained |
| Same raw async iterator with identity mapper | That promise object; mapper output is awaited | Mapped fulfilled value stored |
CHAPTER 10 OF 18 · Inspect promise-valued items
10. Keep async generator yield behaviour separate from a raw iterator
An async generator has its own yield behaviour. In a common async-generator example, yielding a promise results in the consumer receiving the awaited yielded value under the generator's protocol. That can make it look as though Array.fromAsync always unwraps an async iterator's value field. The raw custom iterator from the preceding chapter demonstrates why that broader conclusion does not follow.
The operation that adopted the promise matters. A producer can await work before constructing an iterator result, an async generator can apply its own yield semantics, or a consumer can await a mapper's output. Similar final numbers do not establish that the same component performed the wait. Put the wait at its actual boundary in the diagram.
For a controlled comparison, use one async generator that yields Promise.resolve of five and one raw async iterator that returns a result object containing a designated promise for five. Observe the no-mapper arrays. The friendly number five can appear from the generator, while the raw source can preserve the inner promise reference. An identity mapper changes the latter through its output wait.
This is a productive transfer question because the code can look nearly identical at the surface. The learner must identify the producer protocol and the location of the promise before predicting the consumer result. Do not turn the special raw iterator into a claim about every stream or library source. Inspect the actual source's documented iterator values when moving beyond the deliberately constructed teaching fixtures.
CHAPTER 11 OF 18 · Follow reads and failure
11. Follow array-like reads and awaited mapping in order
A non-iterable array-like source supplies a length and indexed properties. Array.fromAsync obtains its traversal extent, then processes indexed values and mapper results in sequence. An earlier awaited mapper can change a later indexed property before that later property is read. The captured extent is not a simultaneous snapshot of every initial value.
Use a plain object with length two and values two and four. On the first mapper call, wait on a controlled gate, then change source index one to nine before returning four. The second indexed read occurs later and obtains nine. A doubling mapper can therefore produce four and eighteen in this intentional teaching case.
Change the first mapper so it adds index two and increases the plain object's length to three. That new position is outside the initially obtained extent for the array-like path. Its addition does not automatically extend this conversion. A normal array follows an iteration path, so a length-change comparison needs a separate fixture rather than silently importing this plain-object prediction.
The useful evidence records extent, each read, each mapper result and any intervening effect. A final printout of the source cannot reconstruct which value the mapper saw earlier. Nor does an eventual completed array prove that the source stayed unchanged while the conversion was pending. The contract gives a processing sequence; the application must decide whether changing source data during that sequence is intended or should be prevented.
CHAPTER 12 OF 18 · Follow reads and failure
12. Distinguish missing positions from missing results
For a plain array-like object with a covered but absent indexed property and no inherited replacement, the read produces undefined. The mapper can receive that undefined after the relevant input processing. Returning undefined from the mapper is another route to the same output value. Those histories should remain distinguishable in a diagnostic even when the final array contains identical-looking entries.
A successful array result has positions for the collected mapped values rather than treating every absent source position as an instruction to skip the mapper. Record callback counts and own-property presence in a small fixture. The existing sparse-array guides explain why different methods have different visitation contracts; an assumption borrowed from Array.prototype.map can be wrong for this conversion.
Inherited indexed properties can affect ordinary lookup. A local prototype supplying a value at an otherwise absent own index can be observed through the array-like path. A value read from that prototype is not evidence that the source owned the property. Keep own presence and lookup result in separate fields when diagnosing where an unexpected value originated.
A practical data contract should represent important missingness deliberately. Unknown quantity, supplied zero and an unavailable record are different application states. The conversion mechanism can collect values without validating that distinction. If the mapper resolves a missing value to a default, document that policy and retain the raw evidence when required, rather than later crediting the array conversion with a business decision it did not make.
CHAPTER 13 OF 18 · Follow reads and failure
13. Observe mapper rejection and bounded iterator cleanup
A mapper can throw or return a promise that rejects. The conversion promise then rejects under the relevant failure path rather than fulfilling with a completed array. Earlier producer and mapper effects may already have happened. Sequential waiting does not make the operation a transaction that rolls back every preceding action.
For a controlled cleanup test, use a finite async generator with a finally block that records local cleanup. Make the mapper reject on the first supplied item and observe the outer promise's rejection. The iterator-closing path for this bounded mapper failure can run the generator's cleanup. Record the supplied error identity, mapped-item count and cleanup marker together.
Do not generalise that every failure at every iterator stage performs the same cleanup. A rejection from a custom next method, a malformed iterator result and an error during mapper processing are different paths that must be analysed under their actual algorithms. The linked iterator-closing specialist explains the value of keeping failure stages distinct, while this example concerns the stated mapper-failure case.
A local cleanup marker also does not prove that a remote producer stopped, a file was committed or another system reversed earlier work. Those outcomes need the producer's own supported lifecycle and evidence. The useful teaching result is narrow but definite: this controlled failure rejected with the supplied reason after these earlier events and this observed local cleanup. That is sufficient to guide the next investigation without inventing a universal cancellation guarantee.
CHAPTER 14 OF 18 · Test lifetime and practical transfer
14. Keep a pending wait separate from a timeout or abort policy
If a value or mapper result never settles, the sequential conversion can remain pending at that point and never request another item. Array.fromAsync does not automatically invent a timeout appropriate to the task. A gate left closed in a test demonstrates pending work, not successful completion, failure or cancellation.
For an application, decide which component owns timeout and cancellation behaviour. A producer or mapper may accept a supported signal and cooperate with it, but the conversion's ordinary signature does not turn a signal-shaped object into an automatic stop instruction. Passing extra data somewhere is not evidence that the waiting work knows how to respond.
Keep the original controlled fixture bounded by releasing its gate or deliberately observing a supplied rejection. A teaching test should not leave unattended work indefinitely pending merely to prove that waiting is sequential. The relevant observation is available while the mapper is known to be blocked, after which the fixture can be completed and its promises observed.
For a real workflow, define what happens to earlier successful results and effects if a later item stalls. Do users see a partial preview, wait for a complete collection or receive an explicit recoverable error? Those are product requirements outside the method's basic array-building contract. Naming them prevents a small language operation from silently becoming the entire reliability policy for a service.
CHAPTER 15 OF 18 · Test lifetime and practical transfer
15. Remember that the completed collection is shallow and finite
The method collects values into an array; it does not automatically deep-clone objects returned by a mapper. A mapper can return a source record reference, and the completed array can share that record with another part of the application. Awaiting the array-building promise establishes completion of that conversion, not permanent immutability of every field reachable from the result.
A controlled reference test returns the same local record from a mapper, awaits completion and then changes a non-key label through the original reference. The array observes the changed field. Replacing the original variable with a different object is a separate operation that does not rewrite the existing array element. This is the familiar distinction between retaining an object value and retaining instructions to reread a variable.
Collection also requires a completion condition if the caller expects a fulfilled finite array. An unbounded async source is not made finite by choosing Array.fromAsync. Even a bounded rate of pulls can accumulate a large result if the source is long. Sequential consumption and bounded total memory are different claims; this article does not measure or promise a resource ceiling.
For a long-running source, the task may need incremental processing rather than complete materialisation. Choose that architecture according to the result the user needs. A short catalogue import can reasonably require a completed array, while an ongoing event feed may not. The method is useful within its contract, but the output container does not decide the correct lifetime or storage policy for every source.
CHAPTER 16 OF 18 · Test lifetime and practical transfer
16. Test event boundaries with gates rather than elapsed-time folklore
The strongest central assertion is that, after the first mapper has signalled it started and while its controlled output remains pending, the next source pull has not occurred. Releasing the gate and awaiting the outer promise then verifies continued consumption and the final array. This directly tests the boundary named in the article title.
Additional fixtures should change one source assumption at a time. Compare sync iterable promises with a raw async iterator containing a promise-valued field. Compare no mapper with an identity mapper. Compare a lazy generator that creates work on pull with an array containing already-started promises. The outputs and event logs together reveal which waits belong to which component.
Record the runtime used for executable checks. A result in one current JavaScript runtime establishes those controlled language observations there; it does not certify every browser version or a deployed site's actual bundle. Check the target environment's availability and behaviour when applying the lesson to a real service. Avoid copying a compatibility claim into a product requirement without verifying that environment.
Do not assert a universal total duration from a timer demonstration. Timers, task creation, asynchronous producer behaviour and resource limits can all affect elapsed time. The gate design deliberately avoids those assumptions. If performance is the real question, design a separate benchmark with an understood workload and report what it actually measured rather than treating a two-item sequencing test as a speed comparison.
CHAPTER 17 OF 18 · Test lifetime and practical transfer
17. Transfer the reasoning to a staged catalogue preview
Imagine a fictional import preview with two records, A and B. The producer supplies A only when pulled. The mapper checks A through a controlled asynchronous operation and returns its preview record. The requirement says that B must not be requested until A's mapping has completed. The sequential pull-and-awaited-output contract matches that stated relationship in the bounded example.
Now change the requirement: A and B are independent requests that should both start immediately, while the final display preserves input order. The earlier sequential design still has defined behaviour, but it no longer expresses the newly stated start policy. A deliberate eager or bounded-concurrency design may be appropriate, with its own error and cancellation decisions. Do not call the method broken because the product requirement changed.
For another change, give the preview an array of already-started promises instead of a lazy producer. Even if the same Array.fromAsync expression remains, the claim that only one underlying operation has started no longer follows. Restore the creation log to the evidence. This is a valuable diagnostic because a source refactor can change concurrency without changing the consumer line that developers are watching.
The learner's task is to describe start policy, pull policy, mapper completion and result policy separately. If any one is missing, identify the additional contract needed before promising the preview's behaviour. The answer becomes a small design explanation rather than an incantation involving await, and it remains useful when the source, mapper or required user experience changes.
A final evidence check records whether the source factory was called again between tests. Fresh iterator state is part of the fixture, just as fresh gates and counters are. Equal final values do not prove two tests exercised the same history if one reused an exhausted or partly consumed source. Preserve the setup along with the observed log.
CHAPTER 18 OF 18 · Test lifetime and practical transfer
18. Finish with an explanation of who waited for what
A precise short report names the selected source protocol, records the next request and obtained value, and shows that the mapper result was awaited before the consumer continued to another item. For sync iterable and array-like sources, include the input-value wait. For a raw async iterator's promise-valued field, preserve the distinct no-mapper and mapped-output cases.
Keep outer fulfilment, nested values and external effects separate. A completed array may contain shared objects or, under the stated raw async path without mapping, promise values. A rejected conversion may have earlier effects. A pending conversion may be waiting for one unsettled operation without any automatic timeout. Each state has a defined meaning and a different next question.
For independent practice, build a finite two-item producer and a first mapper gate. Predict the event log before releasing the gate, then complete the fixture and explain the final done request. Replace the source with already-created promises and identify which concurrency claim must weaken. Finally add an identity mapper to a raw async iterator's promise-valued field and explain why that seemingly harmless change can add a wait.
The lasting habit is to locate every asynchronous boundary instead of treating await as a general promise that the whole system is serial, cancelled or complete. Array.fromAsync supplies a specific consumption and mapping contract. Once pulls, produced values, mapper outputs and already-started work are separate, that contract becomes something a learner can predict, test and use for the actual task with clear limits.
Continue through the verified reading route
Array.from mapping during source consumption
Array.from mapping during source consumption. Establish the synchronous materialisation contrast before applying the awaited contract here.
Promise.all rejection and cancellation
Promise.all rejection and cancellation. Separate aggregate completion from the lifecycle of work that has already begun.
Iterator closing on early for-of exit
Iterator closing on early for-of exit. Study the iterator cleanup boundary while retaining each operation’s own failure contract.
Frequently asked questions
Does Array.fromAsync start every mapper at once?
Its ordinary consumption awaits each mapper result before continuing to the next item. Already-started work inside supplied promises can still progress independently.
Is it the same as Promise.all on any async source?
No. Source requirements and consumption policies differ. Use a compatible synchronous iterable for a fair eager-versus-sequential task-creation comparison.
Does an async iterator's value field always get unwrapped?
A raw async iterator can supply a promise as its value field. Without a mapper, that promise can remain an array element. A mapper's returned promise is awaited, so an identity mapper can change this boundary.
Why can an async generator produce a plain value from yield Promise.resolve?
The generator has its own yield semantics. Identify which producer or consumer step adopted the promise rather than attributing every wait to Array.fromAsync.
Does rejection undo earlier mapper effects?
No general rollback is supplied. Observe the rejection and preserve the relevant progress and cleanup evidence for the actual failure path.
Does sequential pulling guarantee bounded memory or cancellation?
No. A long finite source can still build a large array, an unbounded source may never finish, and external cancellation needs the producer's supported contract.
Authoritative sources and further reading
ECMAScript: Array.fromAsync. Primary algorithm for protocol selection, awaited pulls, mapper results and array-like reads.
MDN: Array.fromAsync. Reference contrasts for sequential consumption, promise-valued source items and async generator behaviour.
Return to the Websites subject guide, The Gold Standard of Education, or the How X Works hub.