An await pauses the current function until a promise settles. Whether operations overlap depends on when each promise is created, because a promise represents work that has already started.
- Sequential.
for (const x of xs) await work(x)callsworkfor the next item only after the previous one has finished. Total time is the sum. Use it when each step needs the previous result, or when order and rate matter. - Parallel.
await Promise.all(xs.map(work))callsworkfor every item first, which starts all operations, then waits for all of them. Total time is roughly the slowest one. The results keep input order. - Also parallel. Create the promises first, then await them one by one:
const a = f(); const b = g(); await a; await b;. The combinator is only a convenient way to wait. The danger is below. forEachdoes not wait. It calls the callback and ignores the returned promise. Code afterforEachruns before the async callbacks finish, and errors in them become unhandled rejections.for await (const x of source)waits for each item in turn. Use it for async iterables, such as streams and paginated sources. If the source is a sync iterable of promises, it awaits each one in order, but the promises already run.- Unbounded parallelism is not free.
Promise.all(10_000_items.map(fn))starts everything at once. Limit concurrency instead (see the task runner question).
Pick by dependency: dependent steps are sequential, independent steps are parallel.