Alif Fitri
Alif Fitri

Reputation: 23

How to make forEach wait for subscribe to finish?

I'm using Angular 6 and my database is Firebase. Now I try to show each of the room's names according to its building. For example:

Building A
Room 1
Room 2
Building B
Room 1
Room 2

But every time I try, it will show all the building names first, and only then it will show the room's names. This is what I got for now:

Building A
Building B
Room 1
Room 2

I find out this happens because forEach keeps skipping the subscribe function.

I have attempted to use promise and await, but it doesn't work. Maybe because I use it in a wrong way? I'm not so sure how to use promise in a correct way.

async loadData(){
    this.navigationService.user
        .subscribe( user => {
        console.log(user);
        this.user = user;
        this.navigationService.getBuildings(this.user.orgId)
            .subscribe( building => {
            this.navMasterBuilding = [];
            this.buildings = building;
            this.buildings.forEach( async building2 => {
                this.completeBuildingId = building2.completeBuildingId;
                this.buildingName = building2.buildingName;
                console.log(building2.buildingName);
                await new Promise ((resolve, reject) => { 
                    this.navigationService.getLocation(this.user.orgId, this.completeBuildingId)
                    .subscribe(location => {
                    console.log('room' + location);
                    this.navMasterLocation = [];
                    });
                    resolve();
                });   
            });
        });
    });
}

I have try to delay it by using setTimeout, but it still does't work.

Upvotes: 2

Views: 6856

Answers (1)

David Walschots
David Walschots

Reputation: 12640

Array.forEach only works with synchronous functions. Change it to using the for...of syntax which does work. Next to that you resolve the Promise before the call to getLocation finished and the code in subscribe ran. Change the code to call resolve within the getLocation.subscribe.

for(const building2 of buildings) {
    console.log(building2.buildingName);
    await new Promise ((resolve, reject) => { 
        this.navigationService.getLocation(this.user.orgId, this.completeBuildingId)
            .subscribe(location => {
                console.log('room' + location);
                this.navMasterLocation = [];

                resolve();
             });                    
    });   
}

Note that it can be simplified to:

for(const building2 of buildings) {
    console.log(building2.buildingName);
    const location = await this.navigationService.getLocation(this.user.orgId, this.completeBuildingId).toPromise();
    console.log('room' + location);
    this.navMasterLocation = [];             
}

Also do note that I'm not quite sure why you use this.completeBuildingId instead of a local variable. Given that it gets overwritten in each iteration this seems a bit useless to me.

Upvotes: 10

Related Questions