Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
5.2k views
in Technique[技术] by (71.8m points)

angular - How to wait for subscriptions inside a for loop to complete before proceeding

I have a subscription inside of a for loop that fetches JSON data as a series of "category data" from an external source, then filters that data based on the users current location. What I need is to wait for all of the subscriptions to complete, and then and only then can my app proceed. Right now, it does not wait for all of the subscriptions to complete, it only completes a few, and then proceeds, while the other subscriptions continue in the background.

I've tried the following "brute force" method, where i know a certain number of categories will be added to the filtered array, and it works, but I don't know how to make it work for any situation.

Here's my code:

getMultipleCategoryData(categoryIds: string[]) {
for (let i = 0; i < this.waypointIds.length; i++) {
//after user selects their categories, its added to waypointNames, and occurences() gets the # of occurences of each waypoint name
  let occurences = this.occurences(this.waypointNames[i], this.waypointNames);
  this.categoryApi.getCategoryData(this.waypointIds[i]).toPromise().then(data => {

    let filteredLocs = data.locations.filter(loc => this.distanceTo(loc, this.hbLoc) < MAX_RADIUS);
    let foundLocs = [];
    //fill filteredLocs with first n, n = occurences, entries of data 
    for (let n = 0; n < occurences; n++) {
      if (filteredLocs[n] != undefined) {
        foundLocs[n] = filteredLocs[n];
      }
    }
    //find locations closest to hbLoc (users current location), and add them to waypoints array
    for (let j = 0; j < foundLocs.length; j++) {
      for (let k = 0; k < filteredLocs.length; k++) {
        if (this.distanceTo(this.hbLoc, filteredLocs[k]) < this.distanceTo(this.hbLoc, foundLocs[j]) && foundLocs.indexOf(filteredLocs[k]) < 0) {
          foundLocs[j] = filteredLocs[k];
        }
      }
    }
    if (foundLocs.length > 0 && foundLocs.indexOf(undefined) < 0) {
      for (let m = 0; m < foundLocs.length; m++) {
        this.waypointLocs.push(foundLocs[m]);
      }
    }
  }).then(() => { 
    //this hardcoded, brute force method works, but i'd need it to be more elegant and dynamic
    if (this.waypointLocs.length >= 5) {
      let params = { waypointLocs: this.waypointLocs, hbLoc: this.hbLoc };
      this.navCtrl.push(MapPage, params);
    }
  });
}
}

And the categoryApi.getCategoryData method:

getCategoryData(categoryId): Observable<any> {
    // don't have data yet
    return this.http.get(`${this.baseUrl}/category-data/${categoryId}.json`)
        .map(response => {
            this.categoryData[categoryId] = response.json();
            this.currentCategory = this.categoryData[categoryId];
            return this.currentCategory;
        });
}

Everything is working fine except waiting for the subscriptions to complete, I'd really like a way to determine when all of the subscriptions have completed. Any help is appreciated!

See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

You can collect all observables in an array and use forkJoin to wait for all of them to complete:

let observables: Observable[] = [];
for (let i = 0; i < this.waypointIds.length; i++) {
    observables.push(this.categoryApi.getCategoryData(this.waypointIds[i]))
}
Observable.forkJoin(observables)
    .subscribe(dataArray => {
        // All observables in `observables` array have resolved and `dataArray` is an array of result of each observable
    });

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...