显然,给定一个列表 l
和一个返回 Promise 的函数 f
,我可以这样做:
Promise.all(l.map(f));
困难的部分是,我需要按顺序映射每个元素。也就是说,第一个元素的映射必须在下一个元素开始之前解析。我想防止任何并行性。
我知道如何做到这一点,我将作为答案给出,但我不确定这是一个好的答案。
编辑:有些人的印象是,由于 Javascript 本身是单线程的,因此 Javascript 中不可能并行。
考虑以下代码:
const delay = t => new Promise(resolve => setTimeout(resolve, t));
mapAsync([3000, 2000, 1000], delay).then(n => console.log('beep: ' + n));
mapAsync()
的简单实现会导致每秒打印一次“beep”,持续三秒 - 数字按升序排列 - 但正确 蜂鸣声的间隔时间会在六秒内逐渐增加,数字按降序排列。
举一个更实际的例子,想象一个调用 fetch()
并在包含数千个元素的数组上调用的函数。
进一步编辑:
有人不相信我,所以 here是 fiddle 。
最佳答案
const mapAsync = (l, f) => new Promise((resolve, reject) => {
const results = [];
const recur = () => {
if (results.length < l.length) {
f(l[results.length]).then(v => {
results.push(v);
recur();
}).catch(reject);
} else {
resolve(results);
}
};
recur();
});
编辑: Tholle 的评论让我发现了这个更加优雅且(我希望)anti-pattern -免费解决方案:
const mapAsync = (l, f) => {
const recur = index =>
index < l.length
? f(l[index]).then(car => recur(index + 1).then(cdr => [car].concat(cdr)))
: Promise.resolve([]);
return recur(0);
};
进一步编辑:
适当命名的 Try-catch-finally 建议使用 reduce
进行更简洁的实现。欢迎进一步改进。
const mapAsync2 = (l, f) =>
l.reduce(
(promise, item) =>
promise.then(results =>
f(item).then(result => results.concat([result]))),
Promise.resolve([])
);
关于javascript - 如何将异步函数映射到列表上?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/41908706/