0

I have an AngularJS application, which I use promises to get data from firebase database.

Here is my home-controller:

$scope.wallets;

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

These are my two methods inside an service I call 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);
        });
    });
};

The second method, getWalletById, receive an wallet ID and return an wallet object from the firebase database. this mehod is called on the first method, getWalletsByUserId inside a for loop, which should wait for the second method to return the wallet before iterate to the next, so it can push it into the array. The problem is that it dont wait and the code execute the .then() method on the home-controller before the resolving of the getWalletById, leaving the $scope.wallets empty.

Any advice?

georgeawg
  • 48,608
  • 13
  • 72
  • 95
NameTopSecret
  • 310
  • 5
  • 15
  • 2
    `Promise.all()` is your friend for getting notified when a whole `for` loop of promises is actually done. There are dozens of answers here that show you how to do that if you can't figure it out after reading about `Promise.all()`. – jfriend00 Oct 02 '17 at 03:18
  • Use [$q.when](https://docs.angularjs.org/api/ng/service/$q#when) to convert ES6 promises to promises that are integrated with AngularJS and its digest cycle. See [AngularJS display a promise from Firebase](https://stackoverflow.com/questions/43734062/angualrjs-display-a-promise-from-firebase/43742980#43742980). – georgeawg Oct 02 '17 at 12:43
  • The latest version of the Firebase API already returns ES6 promises. There is no need to manufacture one with `new Promise(function(resolve,reject){})`. – georgeawg Oct 02 '17 at 12:57

2 Answers2

1

Use $q.all() to wait for all sub-promises to complete

$q.all(walletIds.map(function(id){
  return getWalletById(id);
})).then(function(wallets){
  ...
})
Vasily Liaskovsky
  • 2,248
  • 1
  • 17
  • 32
1

Instead of manufacturing an ES6 promise from the ref.on method, use the ref.once method and bring it into the AngularJS execution context with $q.when:

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

Only operations which are applied in the AngularJS execution context will benefit from AngularJS data-binding, exception handling, property watching, etc.


Use $q.all and promise chaining in the parent function:

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

The .then method returns a new promise which is resolved or rejected via the return value of the successCallback, errorCallback (unless that value is a promise, in which case it is resolved with the value which is resolved in that promise using promise chaining).

georgeawg
  • 48,608
  • 13
  • 72
  • 95