为什么这个 map reduce Promises 数组不起作用,但只是减少它呢?
why this map reduce Promises array does not work, but just reducing it does?
假设您有一个异步进程,它是 n 个异步步骤的级联:
function step1(item){
return new Promise((resolve, reject)=>{
setTimeout(()=>{
resolve('step 1 fort item ' + item);
});
});
}
function step2(item){
return new Promise((resolve, reject)=>{
setTimeout(()=>{
resolve('step 2 for item ' + item);
},1000);
});
}
function step3(item){
return new Promise((resolve, reject)=>{
setTimeout(()=>{
resolve('step 3 for item ' + item);
},1000);
});
}
function processItem(item){
let steps = [step1, step2, step3];
return steps.reduce((current, next) => {
return current.then(res => {
console.log(res);
return next(item);
}).then(res => {
console.log(res);
});
},Promise.resolve());
}
所以,现在您有了一个项目数组,您想要处理所有项目,将函数 processItem 应用于每个项目。但是为了限制,所有进程必须按顺序执行,前一个完成后开始。
好吧,如果我这样实现的话:
let items = [1, 2, 3];
items.map(i => processItem(i)).reduce((p, next) => {
return p.then(() => {
return next;
});
}).then(() => {
// all finished
});
你得到这个输出:
step 1 for item 1
step 1 for item 2
step 1 for item 3
step 2 for item 1
step 2 for item 2
step 2 for item 3
step 3 for item 1
step 3 for item 2
step 3 for item 3
只需要 3 秒,而不是预期的 9 秒。
与此同时,如果您避免执行地图步骤,则会得到预期的结果:
let items = [1, 2, 3];
items.reduce((promise, nextItem) => {
return promise.then(() => {
return processItem(nextItem);
});
}, Promise.resolve()).then(() => {
// all finished
});
结果:
step 1 for item 1
step 2 for item 1
step 3 for item 1
step 1 for item 2
step 2 for item 2
step 3 for item 2
step 1 for item 3
step 2 for item 3
step 3 for item 3
并花费了预期的 9 秒。
为什么会这样?我认为当你将一个承诺返回函数映射到一个数组时,你会得到一个承诺数组,所以,reduce 匿名函数的第一个和第二个参数是承诺,你可以像我在上面的第一个例子中所做的那样。我对此有点困惑。
.map
正在调用传递给它的 函数 ,如果您不想在这一步调用 processItem
,您需要这样做再次包装,即
let items = [1, 2, 3];
items.map(i => () => processItem(i)).reduce(
(p, next) => p.then(next),
Promise.resolve()
).then(() => {
// all finished
});
谢谢@Paul S.
以你的回答为基础,我想我更喜欢包装原来的processItem
函数:
function processItem(item){
return function(){
let steps = [step1, step2, step3];
return steps.reduce((current, next) => {
return current.then(res => {
console.log(res);
return next(item);
}).then(res => {
console.log(res);
});
},Promise.resolve());
}
}
然后 map.reduce 保持清洁:
items = [1, 2, 3];
items.map(i => processItem(i)).reduce((p, next) => {
return p.then(() => {
return next();
});
}, Promise.resolve()).then(() => {
// all finished
});
假设您有一个异步进程,它是 n 个异步步骤的级联:
function step1(item){
return new Promise((resolve, reject)=>{
setTimeout(()=>{
resolve('step 1 fort item ' + item);
});
});
}
function step2(item){
return new Promise((resolve, reject)=>{
setTimeout(()=>{
resolve('step 2 for item ' + item);
},1000);
});
}
function step3(item){
return new Promise((resolve, reject)=>{
setTimeout(()=>{
resolve('step 3 for item ' + item);
},1000);
});
}
function processItem(item){
let steps = [step1, step2, step3];
return steps.reduce((current, next) => {
return current.then(res => {
console.log(res);
return next(item);
}).then(res => {
console.log(res);
});
},Promise.resolve());
}
所以,现在您有了一个项目数组,您想要处理所有项目,将函数 processItem 应用于每个项目。但是为了限制,所有进程必须按顺序执行,前一个完成后开始。
好吧,如果我这样实现的话:
let items = [1, 2, 3];
items.map(i => processItem(i)).reduce((p, next) => {
return p.then(() => {
return next;
});
}).then(() => {
// all finished
});
你得到这个输出:
step 1 for item 1
step 1 for item 2
step 1 for item 3
step 2 for item 1
step 2 for item 2
step 2 for item 3
step 3 for item 1
step 3 for item 2
step 3 for item 3
只需要 3 秒,而不是预期的 9 秒。
与此同时,如果您避免执行地图步骤,则会得到预期的结果:
let items = [1, 2, 3];
items.reduce((promise, nextItem) => {
return promise.then(() => {
return processItem(nextItem);
});
}, Promise.resolve()).then(() => {
// all finished
});
结果:
step 1 for item 1
step 2 for item 1
step 3 for item 1
step 1 for item 2
step 2 for item 2
step 3 for item 2
step 1 for item 3
step 2 for item 3
step 3 for item 3
并花费了预期的 9 秒。
为什么会这样?我认为当你将一个承诺返回函数映射到一个数组时,你会得到一个承诺数组,所以,reduce 匿名函数的第一个和第二个参数是承诺,你可以像我在上面的第一个例子中所做的那样。我对此有点困惑。
.map
正在调用传递给它的 函数 ,如果您不想在这一步调用 processItem
,您需要这样做再次包装,即
let items = [1, 2, 3];
items.map(i => () => processItem(i)).reduce(
(p, next) => p.then(next),
Promise.resolve()
).then(() => {
// all finished
});
谢谢@Paul S.
以你的回答为基础,我想我更喜欢包装原来的processItem
函数:
function processItem(item){
return function(){
let steps = [step1, step2, step3];
return steps.reduce((current, next) => {
return current.then(res => {
console.log(res);
return next(item);
}).then(res => {
console.log(res);
});
},Promise.resolve());
}
}
然后 map.reduce 保持清洁:
items = [1, 2, 3];
items.map(i => processItem(i)).reduce((p, next) => {
return p.then(() => {
return next();
});
}, Promise.resolve()).then(() => {
// all finished
});