我知道这不在Array.map
的范围内,但我想等到上一个项目完成其承诺之后再开始下一个项目。碰巧我需要等待前一个条目保存在数据库中才能向前移动。
const statsPromise = stats.map((item) => {
return playersApi.getOrAddPlayer(item, clubInfo, year); //I need these to wait until previous has finished its promise.
});
Promise.all(statsPromise)
.then((teamData) => {
..//
});
playersApi.getOrAddPlayer
返回new Promise
修改
在阅读更多内容时,显示playerApi.getOrAddPlayer
似乎很重要getOrAddPlayer: function (item, clubInfo, year) {
return new Promise((resolve, reject) => {
var playerName = item.name.split(' '),
fname = playerName[0].caps(),
sname = playerName[1].caps();
Players.find({
fname: fname,
sname: sname,
}).exec()
.then(function(playerDetails, err){
if(err) reject(err);
var savePlayer = new Players();
//stuff
savePlayer.save()
.then(function(data, err){
if(err) reject(err);
item._id = data._id;
resolve(item);
});
});
});
}
答案 0 :(得分:10)
您可以使用缩小而不是映射来实现此目的:
stats.reduce(
(chain, item) =>
// append the promise creating function to the chain
chain.then(() => playersApi.getOrAddPlayer(item, clubInfo, year)),
// start the promise chain from a resolved promise
Promise.resolve()
).then(() =>
// all finished, one after the other
);
演示:
const timeoutPromise = x => {
console.log(`starting ${x}`);
return new Promise(resolve => setTimeout(() => {
console.log(`resolving ${x}`);
resolve(x);
}, Math.random() * 2000));
};
[1, 2, 3].reduce(
(chain, item) => chain.then(() => timeoutPromise(item)),
Promise.resolve()
).then(() =>
console.log('all finished, one after the other')
);

如果需要累积值,可以通过缩减传播结果:
stats
.reduce(
(chain, item) =>
// append the promise creating function to the chain
chain.then(results =>
playersApi.getOrAddPlayer(item, clubInfo, year).then(data =>
// concat each result from the api call into an array
results.concat(data)
)
),
// start the promise chain from a resolved promise and results array
Promise.resolve([])
)
.then(results => {
// all finished, one after the other
// results array contains the resolved value from each promise
});
演示:
const timeoutPromise = x => {
console.log(`starting ${x}`);
return new Promise(resolve =>
setTimeout(() => {
console.log(`resolving result for ${x}`);
resolve(`result for ${x}`);
}, Math.random() * 2000)
);
};
function getStuffInOrder(initialStuff) {
return initialStuff
.reduce(
(chain, item) =>
chain.then(results =>
timeoutPromise(item).then(data => results.concat(data))
),
Promise.resolve([])
)
}
getStuffInOrder([1, 2, 3]).then(console.log);

变体#1:Array.prototype.concat
看起来更优雅但会在每个连接上创建一个新数组。为了提高效率,您可以使用Array.prototype.push
以及更多样板:
stats
.reduce(
(chain, item) =>
chain.then(results =>
playersApi.getOrAddPlayer(item, clubInfo, year).then(data => {
// push each result from the api call into an array and return the array
results.push(data);
return results;
})
),
Promise.resolve([])
)
.then(results => {
});
演示:
const timeoutPromise = x => {
console.log(`starting ${x}`);
return new Promise(resolve =>
setTimeout(() => {
console.log(`resolving result for ${x}`);
resolve(`result for ${x}`);
}, Math.random() * 2000)
);
};
function getStuffInOrder(initialStuff) {
return initialStuff
.reduce(
(chain, item) =>
chain.then(results =>
timeoutPromise(item).then(data => {
results.push(data);
return results;
})
),
Promise.resolve([])
);
}
getStuffInOrder([1, 2, 3]).then(console.log);

变体#2:您可以将results
变量提升到较高范围。这将消除嵌套函数的需要,以便在累积数据时通过最近的闭包使results
可用,而是使其全局可用于整个链。
const results = [];
stats
.reduce(
(chain, item) =>
chain
.then(() => playersApi.getOrAddPlayer(item, clubInfo, year))
.then(data => {
// push each result from the api call into the globally available results array
results.push(data);
}),
Promise.resolve()
)
.then(() => {
// use results here
});
演示:
const timeoutPromise = x => {
console.log(`starting ${x}`);
return new Promise(resolve =>
setTimeout(() => {
console.log(`resolving result for ${x}`);
resolve(`result for ${x}`);
}, Math.random() * 2000)
);
};
function getStuffInOrder(initialStuff) {
const results = [];
return initialStuff.reduce(
(chain, item) =>
chain
.then(() => timeoutPromise(item))
.then(data => {
results.push(data);
return results;
}),
Promise.resolve()
);
}
getStuffInOrder([1, 2, 3]).then(console.log);

答案 1 :(得分:1)
如果您使用promise库可以,则可以使用Promise.mapSeries by Bluebird来解决此问题。
示例:
const Promise = require("bluebird");
//iterate over the array serially, in-order
Promise.mapSeries(stats, (item) => {
return playersApi.getOrAddPlayer(item, clubInfo, year));
}).then((teamData) => {
..//
});
答案 2 :(得分:1)
你可以使用一种递归:
function doStats([head, ...tail]) {
return !head ? Promise.resolve() :
playersApi.getOrAddPlayer(head, clubInfo, year)
.then(() => doStats(tail));
}
doStats(stats)
.then(() => console.log("all done"), e => console.log("something failed", e));
另一种经典方法是使用reduce
:
function doStats(items) {
return items.reduce(
(promise, item) =>
promise.then(() => playersApi.getOrAddPlayer(item, clubInfo, year)),
Promise.resolve());
顺便说一句,您可以相当多地清理getOrAddPlayer
函数,并避免使用promise构造函数反模式:
getOrAddPlayer: function (item, clubInfo, year) {
var playerName = item.name.split(' '),
fname = playerName[0].caps(),
sname = playerName[1].caps();
return Players.find({fname, sname}).exec()
.then(playerDetails => new Players().save())
.then({_id} => Object.assign(item, {_id}));
}
答案 3 :(得分:1)
您可以使用递归解决方案
const statsPromise = (function s(p, results) {
return p.length ? playersApi.getOrAddPlayer(p.shift(), clubInfo, year) : results;
})(stats.slice(0), []);
statsPromise
.then((teamData) => {
//do stuff
});
let n = 0;
let promise = () => new Promise(resolve =>
setTimeout(resolve.bind(null, n++), 1000 * 1 + Math.random()));
let stats = [promise, promise, promise];
const statsPromise = (function s(p, results) {
return p.length ? p.shift().call().then(result => {
console.log(result);
return s(p, [...results, result])
}) : results;
})(stats.slice(0), []);
statsPromise.then(res => console.log(res))
答案 4 :(得分:0)
我给了它一个想法,但我找不到比减少一个更好的方法。
根据你的情况改编它会是这样的:
const players = [];
const lastPromise = stats.reduce((promise, item) => {
return promise.then(playerInfo => {
// first iteration will be undefined
if (playerInfo) {
players.push(playerInfo)
}
return playersApi.getOrAddPlayer(item, clubInfo, year);
});
}, Promise.resolve());
// assigned last promise to a variable in order to make it easier to understand
lastPromise.then(lastPlayer => players.push(lastPlayer));
您可以看到有关此here的一些解释。