devMan
devMan

Reputation: 89

Getting promise from a nested forEach loop?

I'm trying to loop through a Firebase List (using AngularFire2), and I want to get a Promise to indicate when the nested loops completed so I can process on the second action. I'm using the following code:

  console.log('START');
  this.af.database.list('Sites/123123/Users')
    .map(users => {
      users.map(user => {
        console.log('found a user ', user);
        this.af.database.list('registeredUsers/' + user.$key + '/Sites')
          .forEach(sites => {
            sites.forEach(site => {

              if (site.$value == '123123') {
                console.log('found site ', site.$key);
              }
            });
          });
      });
    }).first().toPromise().then(function (x) {
      console.log('ALL DONE')
    });

The output is:

console output

But I'm getting the Promise only after the outer loop, without waiting for the nested loop to complete. Any suggestions?

Upvotes: 0

Views: 1169

Answers (2)

devMan
devMan

Reputation: 89

@Kalle

i tried this way too, also same :

 console.log('START');
  var siteUsers = this.af.database.list('Sites/-KcF5J9SoSHDrUEYO-Ed/Users');
  const siteUsersObservable = Observable.from(siteUsers);

  siteUsersObservable
    .map(users => {
      console.log('users = ', users);
      return users.map(user => {
        console.log('found a user ', user);
        var userSites = this.af.database.list('registeredUsers/' + user.$key + '/Sites');
        const userSitesObservable = Observable.from(userSites);
        return userSitesObservable
          .forEach(sites => {
            console.log('sites = ', sites)
            sites.forEach(site => {
              if (site.$value == '-KcF5J9SoSHDrUEYO-Ed') {
                console.log('found site ', site.$key);
              }
            });
          });
      });
    })
    .first().toPromise().then(x => {
      console.log('ALL DONE')
    });

and the firebase structure is like this:

and my result is :

Upvotes: 1

Kalle
Kalle

Reputation: 3907

You are missing return statements in the map calls, because you are generating new Observables inside the first map on the users observable you should use a mergeMap here:

console.log('START');
this.af.database
    .list('Sites/123123/Users')
    .mergeMap(users => {
        return users.map(user => {
            console.log('found a user ', user);
            return this.af.database.list('registeredUsers/' + user.$key + '/Sites')
                .forEach(sites => {
                    sites.forEach(site => {
                        if (site.$value == '123123') {
                            console.log('found site ', site.$key);
                        }
                    });
                });
        });
    })
    .first().toPromise().then(x => {
        console.log('ALL DONE')
    });

Here I have a simplified version of your code, eliminating dependencies to your firebase app:

//const Rx =  require('rxjs'); // require Rx on nodejs instead of html script tag
const usersObservable = Rx.Observable.from([[{ id: 1, $key: 123 }]]);

const sitesObservable = Rx.Observable.from(
    new Promise((resolve) => {
        setTimeout(() => {
            resolve([{ $value: '123123', $key: 'site1' }, { $value: '0', $key: 'site2' }])
        }, 500);
    })
);

console.log('START');
usersObservable
    .mergeMap((users) => {
        return users.map(user => {
            console.log('found a user ', user);
            return sitesObservable
                .forEach(sites => {
                    sites.forEach(site => {
                        if (site.$value == '123123') {
                            console.log('found site ', site.$key);
                        }
                    });
                });
        });
    })
    .first().toPromise().then(x => {
        console.log('ALL DONE')
    });
<script src="https://cdnjs.cloudflare.com/ajax/libs/rxjs/5.0.1/Rx.js"></script>

Here is the generated output:

START
found a user  {
  "id": 1,
  "$key": 123
}
found site  site1
ALL DONE

With your specific firebase code added your Code should look like this:

console.log('START');
var siteUsers = this.af.database.list('Sites/-KcF5J9SoSHDrUEYO-Ed/Users');

siteUsers
    .mergeMap(users => {
        console.log('users = ', users);
        return users.map(user => {
            console.log('found a user ', user);
            return this.af.database.list('registeredUsers/' + user.$key + '/Sites')
                .forEach(sites => {
                    console.log('sites = ', sites)
                    sites.forEach(site => {
                        if (site.$value == '-KcF5J9SoSHDrUEYO-Ed') {
                            console.log('found site ', site.$key);
                        }
                    });
                });
        });
    })
    .first().toPromise().then(x => {
        console.log('ALL DONE')
    });

Upvotes: 1

Related Questions