Javascript 如何使嵌套异步方法同步运行?
我如何将这个例程包装在一个承诺中,以便只有在获得所有数据时才能解析Javascript 如何使嵌套异步方法同步运行?,javascript,node.js,promise,Javascript,Node.js,Promise,我如何将这个例程包装在一个承诺中,以便只有在获得所有数据时才能解析 var accounts = []; getAccounts(userId, accs => { accs.forEach(acc => { getAccountTx(acc.id, tx => { accounts.push({ 'id': acc.id, 'tx': tx
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;
}
函数getAccountsAllAtOnce(){
var账户=[];
所需var=0;
var done=0;
getAccounts(用户ID,accs=>{
所需=accs.length;
accs.forEach(acc=>{
getAccountTx(附件id,tx=>{
账户推送({
“id”:acc.id,
“tx”:tx
});
完成=完成+1;
});
})
});
while(完成<需要){
//等等
}
归还账户;
}
我将每一步分解为自己的函数,并从每一步返回一个promise或promise数组。例如,getAccounts变为:
function getAccountsAndReturnPromise(userId) {
return new Promise((resolve, reject) => {
getAccounts(userId, accounts => {
return resolve(accounts);
});
});
};
getAccountTx解析为{id,tx}对象数组:
function getAccountTransactionsAndReturnPromise(accountId) {
return new Promise((resolve, reject) => {
getAccountTx(account.id, (transactions) => {
var accountWithTransactions = {
id: account.id,
transactions
};
return resolve(accountWithTransactions);
});
});
};
然后可以使用Promise.all()
和map()
将最后一步解析为所需格式的值数组:
function getDataForUser(userId) {
return getAccountsAndReturnPromise(userId)
.then(accounts=>{
var accountTransactionPromises = accounts.map(account =>
getAccountTransactionsAndReturnPromise(account.id)
);
return Promise.all(accountTransactionPromises);
})
.then(allAccountsWithTransactions => {
return allAccountsWithTransactions.map(account =>{
return {
id: account.id,
tx: tx
}
});
});
}
让我们把这个例程放到一个单独的函数中,以便以后更容易重用它。此函数应返回一个承诺,该承诺将通过帐户数组解决(同时我将尽可能小地修改您的代码): 唯一的区别就是返回一个承诺并在获取所有帐户后解决。然而,当您有很多嵌套回调时,回调往往会使您的代码库具有这种“回调地狱”风格,这使得您很难对此进行推理。您可以使用良好的规程来解决这个问题,但是您可以通过切换到所有异步函数来返回承诺来大大简化它。例如,您的func将如下所示:
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”您当前的回调风格函数(例如,甚至本机节点)。此外,有了新功能,它变得更加容易,因为它允许同步思考:
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));
}
如您所见,我们消除了任何嵌套!它使关于代码的推理变得更加容易,因为您可以按照实际执行的方式阅读代码。然而,所有这三个选项都是等效的,因此,在您的项目和环境中,什么才是最有意义的取决于您。这里您的异步操作在哪里?请参阅全部承诺:使用promisAll包装您的for each,并确保在项目的每次迭代中都返回一个承诺“forEach'您正在用一组对象填充一个名为
accounts
的数组,每个对象都有一个帐户ID和一个事务。取决于消耗此数据的内容,但对于帐户
而言,将其作为帐户
对象的数组更具可读性,每个对象包含一个id
值和一个tx
数组。我希望它被命名为accountTransactions
,而不是accounts
。您不能同步运行异步函数(因此它们会立即返回结果)。你所能做的就是按顺序运行它们。我也设法做到了这一点,但这是最优雅的方式吗?仅仅等待所有数据完成似乎需要做很多工作。实际上,您正在做一些复杂的事情:一个异步调用,然后是一个异步调用数组,然后是数据操作,以将其转换为所需的格式。我相信还有更优雅的方法可以做到这一点——毕竟我只花了20分钟——但它们是相同概念的变体。您如何使用util.promisify
来包装OP的getAccounts()
和getAccountTx()
?查看您链接的文档,很清楚如何使用util.promisify
进行错误优先回调;但对于docs示例,使用util.promisify(“自定义promisified函数”)将函数包装为非错误优先回调,这似乎比仅将调用包装在new Promise()
中会增加更多代码、复杂性和不可读性。我一定错过了什么。
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));
}