如何同步运行嵌套异步方法?

时间:2017-12-03 05:24:50

标签: javascript node.js promise

如何在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;
}

2 个答案:

答案 0 :(得分:2)

让我们把这个例程放到一个单独的函数中,以便以后重用它。这个函数应该返回一个promise,它将通过一组帐户解析(我也会尽可能地修改你的代码):

basename($_SERVER['PHP_SELF'])

唯一的区别就是在获取所有帐户后返回承诺并解决。但是,当你有很多嵌套回调时,回调会使你的代码库具有这种“回调地狱”风格,并且很难对其进行推理。您可以使用良好的规则来解决它,但您可以大大简化它,切换到从所有异步函数返回的promise。例如,您的func将如下所示:

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

它们都是完全等效的,并且有很多库可以“宣传”你当前的回调式函数(例如,bluebird甚至本机节点util.promisify)。此外,使用新的async/await syntax它变得更加容易,因为它允许在同步流中思考:

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

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

答案 1 :(得分:1)

我将每一步拆分成自己的函数,并从每个函数返回一个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
        }
    });
  });
}