You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
81 lines
1.6 KiB
81 lines
1.6 KiB
'use strict'; |
|
const AggregateError = require('aggregate-error'); |
|
|
|
module.exports = async ( |
|
iterable, |
|
mapper, |
|
{ |
|
concurrency = Infinity, |
|
stopOnError = true |
|
} = {} |
|
) => { |
|
return new Promise((resolve, reject) => { |
|
if (typeof mapper !== 'function') { |
|
throw new TypeError('Mapper function is required'); |
|
} |
|
|
|
if (!((Number.isSafeInteger(concurrency) || concurrency === Infinity) && concurrency >= 1)) { |
|
throw new TypeError(`Expected \`concurrency\` to be an integer from 1 and up or \`Infinity\`, got \`${concurrency}\` (${typeof concurrency})`); |
|
} |
|
|
|
const result = []; |
|
const errors = []; |
|
const iterator = iterable[Symbol.iterator](); |
|
let isRejected = false; |
|
let isIterableDone = false; |
|
let resolvingCount = 0; |
|
let currentIndex = 0; |
|
|
|
const next = () => { |
|
if (isRejected) { |
|
return; |
|
} |
|
|
|
const nextItem = iterator.next(); |
|
const index = currentIndex; |
|
currentIndex++; |
|
|
|
if (nextItem.done) { |
|
isIterableDone = true; |
|
|
|
if (resolvingCount === 0) { |
|
if (!stopOnError && errors.length !== 0) { |
|
reject(new AggregateError(errors)); |
|
} else { |
|
resolve(result); |
|
} |
|
} |
|
|
|
return; |
|
} |
|
|
|
resolvingCount++; |
|
|
|
(async () => { |
|
try { |
|
const element = await nextItem.value; |
|
result[index] = await mapper(element, index); |
|
resolvingCount--; |
|
next(); |
|
} catch (error) { |
|
if (stopOnError) { |
|
isRejected = true; |
|
reject(error); |
|
} else { |
|
errors.push(error); |
|
resolvingCount--; |
|
next(); |
|
} |
|
} |
|
})(); |
|
}; |
|
|
|
for (let i = 0; i < concurrency; i++) { |
|
next(); |
|
|
|
if (isIterableDone) { |
|
break; |
|
} |
|
} |
|
}); |
|
};
|
|
|