如何同步运行嵌套的asynchronous方法?

如何将这个例程封装在一个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); } }); }); }); }); } 

唯一的区别就是在所有账户被提取之后,只是回复一个承诺并解决。 但是,当你有很多嵌套的callback时,callback会让你的代码库有这种“callback地狱”的风格,这使得很难推理它。 你可以使用良好的纪律来解决这个问题,但是你可以简化它,大大地切换到从所有asynchronous函数返回promise。 例如,你的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”你目前的callback式函数(例如, 蓝鸟甚至本地Node util.promisify )。 另外,使用新的asynchronous/等待语法,它变得更加容易,因为它允许在同步stream程中考虑:

 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)); } 

正如你所看到的,我们消除了任何嵌套! 它使得代码的推理变得更容易,因为你可以读取代码,因为它将被实际执行。 但是,所有这三个选项都是相同的,所以取决于您,在您的项目和环境中最有意义。

我将每一步分解到它自己的函数中,并从每一个返回一个promise或promise数组。 例如,getAccounts变成:

 function getAccountsAndReturnPromise(userId) { return new Promise((resolve, reject) => { getAccounts(userId, accounts => { return resolve(accounts); }); }); }; 

getAccountTxparsing为{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()将您想要的格式的最后一步parsing为一个值的数组:

 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 } }); }); }