A library is not required to iterate over an array of items asynchronously.
For example, to process this array asynchronously:
const arr = [1, 2, 3];
First, create a generator from an array so that can you yield execution for each item in the array:
/**
* A convenience function for creating a ES2015 Generator to iterate over
* items in an array.
*
* @template T
* @param {T[]} value
*/
function* toIterator<T>(value: T[]) {
const generator = value[Symbol.iterator]();
for (let generated of generator) {
yield generated;
}
}
Then, the key to iterating over the array asynchronously is to have a function call itself on the resolve
of the provided handler
.
/**
* Provides the ability to process an iterator's values asynchronously
* with a predicate, `hander`.
*
* @template T
* @template TReturn
* @template TNext
* @param {Generator<T, TReturn, TNext>} generator
* @param {(arg: T) => Promise<TReturn>} handler
* @returns
*/
async function iterateAsynchronously<T, TReturn, TNext>(
generator: Generator<T, TReturn, TNext>,
handler: (arg: T) => Promise<TReturn>
) {
const iterate: () => Promise<TReturn> = async () => {
const generated = generator.next();
if (generated.done) {
return generated.value;
}
return handler(generated.value).then(iterate);
};
return iterate();
}
If you prefer, you can extend the global Array with one or both of these methods. Another example.
This would allow you to chain more array methods together after processing the items asynchronously:
const run = async () => {
const arr = [1, 2, 3, 4];
console.log((await arr.iterateAsynchronously((val) => new Promise(r => {
setTimeout(r, 2000);
console.log('Processing', val);
}))).reverse());
}
run();
In this case, since the array prototype is modified with the function extension that returns the array itself, you can chain array methods together like you are used to.