Principle:Webdriverio Webdriverio Async Iteration
| Knowledge Sources | |
|---|---|
| Domains | Async_Utilities, Core_Architecture |
| Last Updated | 2026-02-12 00:00 GMT |
Overview
Providing promise-aware array iteration methods that support both concurrent and serial execution of asynchronous operations.
Description
Standard array methods like map, filter, forEach, find, and reduce do not natively handle asynchronous callback functions -- they invoke the callback but do not await the returned promise, leading to incorrect behavior when the callback performs I/O or browser commands. Async Iteration provides drop-in replacements for these array methods that correctly await each callback's promise, with the choice between serial execution (each callback waits for the previous one to complete) and concurrent execution (all callbacks start simultaneously and results are collected when all complete). This is critical in browser automation where operations must often execute sequentially against a shared browser session, but other scenarios (like parallel data fetching) benefit from concurrency.
Usage
This principle applies whenever asynchronous functions must be applied across a collection of items. It is the right choice for iterating over elements returned by a browser query (e.g., clicking each item in a list sequentially), processing configuration arrays where each entry requires async resolution, or performing parallel setup operations across multiple workers. It replaces the common anti-pattern of using Promise.all(array.map(...)) for concurrent cases and for...of loops for serial cases, providing a cleaner, more expressive API.
Theoretical Basis
Async iteration is built on promise chaining for serial execution and promise aggregation for concurrent execution:
- Serial iteration (forEach, map, filter, find, reduce): Each callback is awaited before the next is invoked. This preserves ordering guarantees and prevents race conditions when callbacks share mutable state (such as a browser session):
async function serialMap(array, asyncFn):
results = []
for item in array:
result = await asyncFn(item)
results.append(result)
return results
async function serialFilter(array, asyncPredicate):
results = []
for item in array:
if await asyncPredicate(item):
results.append(item)
return results
async function serialFind(array, asyncPredicate):
for item in array:
if await asyncPredicate(item):
return item
return undefined
- Concurrent iteration (map, forEach): All callbacks are started simultaneously and results are collected via a barrier synchronization:
async function concurrentMap(array, asyncFn):
promises = array.map(item => asyncFn(item))
return awaitAll(promises)
- Reduce semantics: The async reduce preserves the accumulator threading of standard reduce, but awaits both the accumulator promise and the callback promise at each step:
async function asyncReduce(array, asyncFn, initialValue):
accumulator = initialValue
for item in array:
accumulator = await asyncFn(accumulator, item)
return accumulator
The key design decision is defaulting to serial execution in a browser automation context, since most browser operations are not safe to run concurrently within a single session. Concurrent variants are explicitly opt-in for scenarios where parallelism is safe and beneficial.