Spurious Spurious - 3 years ago 80
Javascript Question

ForEach is faster than my Observable returns values

I have the following code:

evaluateLocations() {
const locs = this.currentTour.locIds;
let lastLoc = null;
locs.forEach(loc => {
console.log(lastLoc, loc);
if (lastLoc !== null) {
console.log('if durch', lastLoc, loc);
this.afDb.list('routes', {
query: {
orderByChild: 'idStart',
equalTo: loc
}
}).switchMap(items => {
console.log('switchMap starts', lastLoc, loc); //here the output is always lastLoc = loc while it shouldn't be that
const filtered = items.filter(item => item.idEnd === lastLoc);
const route = filtered[0];
return Observable.of(route);
}).subscribe();
}
lastLoc = loc;
});
}


Now, at the point the
switchMap
comes into play, the
forEach
loop seems to have run through so the filtering is always done wrong.

My logic is:

I want to loop through an array of ids. Then I call the database that gets all the entries where the starting point is
locs[n]
. I then want to filter for
locs[n+1]
and it will return one result.

In theory this should work, but given that I query from a
Firebase
database, the Observable has a time lag in there and I cannot get the values quickly enough. Another thing I suspect and what I read about is the fact that
Observables
might be cancelled if another one is started before the current one is finished. But when I run my code this does not seem to be a problem.

Answer Source

If you want to work that list sequentially, you need to wait for the operation of the Observable to finish. An idiomatic way to do that seems to be converting the Observable to a Promise and to await that:

Applying that to your code:

async evaluateLocations() { // <-- make method async, so we can use await
  const locs = this.currentTour.locIds;
  let lastLoc = null;
  for (let locKey in locs) { // <-- use regular for-in loop
    let loc = locs[locKey];
    console.log(lastLoc, loc);
    if (lastLoc !== null) {
      console.log('if durch', lastLoc, loc);
      await this.afDb.list('routes', { // <-- use await here
        query: {
          orderByChild: 'idStart',
          equalTo: loc
        }
      }).switchMap(items => {
        console.log('switchMap starts', lastLoc, loc); //here the output is always lastLoc = loc while it shouldn't be that
        const filtered = items.filter(item => item.idEnd === lastLoc);
        const route = filtered[0];
        return Observable.of(route);
      }).toPromise(); // <-- convert to Promise
    }
    lastLoc = loc;
  }
}

Don't forget you now need to await evaluateLocations() too in the calling function, if you need to do something afterwards that relies on the outcome of evaluateLocations().

Recommended from our users: Dynamic Network Monitoring from WhatsUp Gold from IPSwitch. Free Download