簡體   English   中英

如何同步運行嵌套異步方法?

[英]How to make run nested asynchronous methods synchronously?

如何在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,它將通過一組帳戶解析(我也會盡可能地修改你的代碼):

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。 例如,您的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));
    });
}

它們都是完全等價的,並且有很多庫可以“宣傳”你當前的回調式函數(例如, bluebird甚至本機Node util.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));
}

如您所見,我們消除了任何嵌套! 它使得代碼的推理變得更加容易,因為您可以讀取實際執行的代碼。 但是,所有這三個選項都是等價的,因此由您決定,在您的項目和環境中最有意義的是什么。

我將每一步都拆分成自己的函數,並從每個函數返回一個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
        }
    });
  });
}

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM