如何将此例程包装在 Promise 中,以便仅在获取所有数据时才解析?
var accounts = [];
getAccounts(userId, accs => {
accs.forEach(acc => {
getAccountTx(acc.id, tx => {
accounts.push({
'id': acc.id,
'tx': tx
});
});
})
});
编辑:如果我这样做有什么问题吗?
function getAccountsAllAtOnce() {
var accounts = [];
var required = 0;
var done = 0;
getAccounts(userId, accs => {
required = accs.length;
accs.forEach(acc => {
getAccountTx(acc.id, tx => {
accounts.push({
'id': acc.id,
'tx': tx
});
done = done + 1;
});
})
});
while(done < required) {
// wait
}
return accounts;
}
最佳答案
让我们把这个例程放到一个单独的函数中,这样以后更容易重用它。此函数应返回一个 promise ,该 promise 将通过帐户数组解决(我也会尽可能小地修改您的代码):
function getAccountsWithTx(userId) {
return new Promise((resolve, reject) => {
var accounts = [];
getAccounts(userId, accs => {
accs.forEach(acc => {
getAccountTx(acc.id, tx => {
accounts.push({
'id': acc.id,
'tx': tx
});
// resolve after we fetched all accounts
if (accs.length === accounts.length) {
resolve(accounts);
}
});
});
});
});
}
唯一的区别只是返回一个 promise 并在获取所有帐户后进行解析。然而,当你有很多嵌套的回调时,回调往往会使你的代码库具有这种“回调 hell ”的风格,并且很难对其进行推理。您可以使用良好的纪律来解决它,但是您可以大大简化它切换到从所有异步函数返回 promise 。例如,您的函数将如下所示:
function getAccountsWithTx(userId) {
getAccounts(userId)
.then(accs => {
const transformTx = acc => getAccountTx(acc.id)
.then(tx => ({ tx, id: acc.id }));
return Promise.all(accs.map(transformTx));
});
}
它们两者是绝对等价的,并且有大量的库可以“promisify”您当前的回调式函数(例如,bluebird 甚至原生 Node util.promisify)。此外,新的 async/await syntax它变得更加容易,因为它允许在同步流程中思考:
async function getAccountsWithTx(userId) {
const accs = await getUserAccounts(userId);
const transformTx = async (acc) => {
const tx = getAccountTx(acc.id);
return { tx, id: acc.id };
};
return Promise.all(accs.map(transformTx));
}
如您所见,我们消除了任何嵌套!它使对代码的推理变得更加容易,因为您可以在实际执行时阅读代码。但是,所有这三个选项都是等效的,因此您可以根据自己的项目和环境选择最有意义的选项。
关于javascript - 如何同步运行嵌套的异步方法?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/47615542/