【问题标题】:How to make JavaScript promise wait for another promise to be resolved inside for() loop如何让 JavaScript 承诺等待在 for() 循环中解决另一个承诺
【发布时间】:2018-03-13 03:23:20
【问题描述】:

我有一个 AngularJS 应用程序,我使用 promise 从 firebase 数据库获取数据。

这是我的home-controller

$scope.wallets;

walletDAO.getWalletsByUserId(auth.uid)
.then(function(wallets){
    $scope.wallets = wallets;
    $scope.$apply();
})
.catch(function(error){
    console.log(error);
});

这是我调用walletDAO的服务中的两个方法:

this.getWalletsByUserId = function(id) {
    return new Promise(function(resolve, reject) {
        //codigo aqui
        var dbRef = database.ref("users/" + auth.currentUser.uid);
        dbRef.on('value', function(data) {
            //console.log("Wallet IDs Retrieved!");
            var userOnDB = data.val();
            var walletIds = userOnDB.wallets;
            var wallets = [];
            for (i = 0; i < walletIds.length; i++) {
                var x = getWalletById(walletIds[i])
                .then(function(wallet){
                    wallets.push(wallet);
                })
                .catch(function(error){
                    console.log(error);
                });
            }
            resolve(wallets);
        }, function(error) {
            reject(error);
        });
    });
};

var getWalletById = function(id) {
    return new Promise(function(resolve, reject) {
        var dbRef = database.ref("wallets/" + id);
        dbRef.on('value', function(data) {
            //console.log("Wallet Retrieved!");
            var wallet = data.val();
            //console.log(wallet);
            resolve(wallet);
        }, function(error) {
            reject(error);
        });
    });
};

第二种方法getWalletById 接收钱包ID 并从firebase 数据库返回一个wallet 对象。这个方法在第一个方法上调用,getWalletsByUserIdfor 循环内,它应该等待第二个方法返回钱包,然后再迭代到下一个,所以它可以将它推入数组。问题是它不等待并且代码在解析getWalletById之前在home-controller上执行.then()方法,使$scope.wallets为空。

有什么建议吗?

【问题讨论】:

  • Promise.all() 是您的朋友,您可以在整个for 的承诺循环实际完成时收到通知。如果您在阅读了有关 Promise.all() 的内容后无法弄清楚,这里有许多答案向您展示了如何做到这一点。
  • 使用 $q.when 将 ES6 Promise 转换为与 AngularJS 及其摘要循环集成的 Promise。见AngularJS display a promise from Firebase
  • 最新版本的 Firebase API 已经返回 ES6 承诺。没有必要用new Promise(function(resolve,reject){})制造一个。

标签: javascript angularjs firebase promise angular-promise


【解决方案1】:

不要从 ref.on 方法制造一个 ES6 承诺,而是使用 ref.once 方法并使用 $q.when 将其带入 AngularJS 执行上下文:

function getWalletById(id) {
    var dbRef = database.ref("wallets/" + id);
    var es6Promise = dbRef.once('value');
    return $q.when(es6Promise);
}

只有在 AngularJS 执行上下文中应用的操作才能受益于 AngularJS 数据绑定、异常处理、属性监视等。


在父函数中使用$q.allpromise chaining

this.getWalletsByUserId = function(id) {
    var dbRef = database.ref("users/" + auth.currentUser.uid);
    var es6Promise = dbRef.once('value')
      .then(function(snapshot)
        //console.log("Wallet IDs Retrieved!");
        var userOnDB = snapshot.val();
        var walletIds = userOnDB.wallets;
        var promiseList = walletIds.map(function(id){
            return getWalletById(id);
        });
        return $q.all(promiseList);
    });
    return $q.when(es6Promise);
};

.then 方法返回一个新的 Promise,它通过 successCallbackerrorCallback 的返回值解决或拒绝(除非该值是一个 Promise,在这种情况下它是使用promise chaining 在该承诺中解析的值解析)。

【讨论】:

    【解决方案2】:

    使用$q.all() 等待所有子promise 完成

    $q.all(walletIds.map(function(id){
      return getWalletById(id);
    })).then(function(wallets){
      ...
    })
    

    【讨论】:

      猜你喜欢
      • 2015-11-13
      • 2020-07-04
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2019-04-29
      • 2020-05-15
      • 2018-03-19
      相关资源
      最近更新 更多