modern-async
Version:
A modern tooling library for asynchronous operations using async/await, promises and async generators
63 lines (59 loc) • 2.35 kB
JavaScript
import assert from 'nanoassert'
import asyncWrap from './asyncWrap.mjs'
/**
* Performs a reduce operation as defined in the `Array.reduce()` method but using an asynchronous
* function as reducer. The reducer will be called sequentially.
*
* @param {Iterable | AsyncIterable} iterable An iterable or async iterable object.
* @param {Function} reducer The reducer function. It will be called with four arguments:
* * `accumulator`: The last calculated value (or the first value of the iterable if no initial value is provided)
* * `value`: The current value
* * `index`: The current index in the iterable. Will start from 0 if no initial value is provided, 1 otherwise.
* * `iterable`: The iterable on which the reduce operation is performed.
* @param {any} [initial] The initial value that will be used as accumulator in the first call to
* `reducer`. If omitted the first element of `iterable` will be used as accumulator and `reducer`
* will only be called from from the second element of the list (as defined in the `Array.reduce()`
* function).
* @returns {Promise} A promise that will be resolved with the result of the reduce operation,
* or rejected if any of the calls to `reducer` throws an exception.
* @example
* import { asyncReduce, asyncSleep } from 'modern-async'
*
* const array = [1, 2, 3]
* const result = await asyncReduce(array, async (v, p) => {
* // these calls will be performed sequentially
* await asyncSleep(10) // waits 10ms
* return v + p
* })
* console.log(result) // prints 6
* // total processing time should be ~ 20ms
*/
async function asyncReduce (iterable, reducer, initial = undefined) {
assert(typeof reducer === 'function', 'iteratee must be a function')
reducer = asyncWrap(reducer)
if (initial !== undefined) {
let current = initial
let i = 0
for await (const el of iterable) {
current = await reducer(current, el, i, iterable)
i += 1
}
return current
} else {
let i = 0
let current
for await (const el of iterable) {
if (i === 0) {
current = el
} else {
current = await reducer(current, el, i, iterable)
}
i += 1
}
if (i === 0) {
throw new TypeError('Reduce of empty array with no initial value')
}
return current
}
}
export default asyncReduce