[Feature Request] Sequential ResultAsync combine
One of our use cases of ResultAsync.combine is to act as a Promise.all for several external API calls, which is great for combining multiple ResultAsyncs running in parallel.
The array of these () => ResultAsync<T,E> can become quite large, so for some scenarios a sequential approach proved to solve some rate-limiting issues.
I would imagine there are other scenarios where sequentially running an array of functions returning ResultAsyncs could be useful, such as:
- Having certain dependencies between each task/function, or where the outcome of one affects the other.
- A primitive way of Rate Limiting/Throttling
- In non idempotent operations, order of each task is important and running them sequentially would be a must. (Specially on longer chains)
- Batch processing
It can also be used as a way to prevent error propagation, if none can fail, as soon as one does no additional calls are made.
The code for this could resemble something like:
static combineSequential<T, E>(
promFuncs: Array<() => ResultAsync<T, E>>
): ResultAsync<T[], E> {
return promFuncs.reduce(
(p, c) =>
p.andThen((acc) => {
return c().map((v) => {
acc.push(v);
return acc;
});
}),
okAsync<T[], E>([] as T[])
);
}
@pyrho came up with this.
ha! great minds think alike. I wrote this a few days ago:
/**
* Run a list of functions sequentially and return immediately when the
* first error is generated.
* If no errors are found, return a list of result values in the same order.
*/
export function runInSequence<T, E>(
fns: ReadonlyArray<() => ResultAsync<T, E>>,
): ResultAsync<Array<T>, E> {
const run = async () => {
const okValues: Array<T> = [];
for (const fn of fns) {
const result = await fn();
if (result.isErr()) {
return err(result.error);
}
okValues.push(result.value);
}
return ok(okValues);
};
return new ResultAsync(run());
}