1

I've got a service in an angular 4 app that is retrieving a variable number of pages from a back-end API.

I've looked at the answer here Angular 2 - Chaining http requests, which suggests using a flatMap, but this approach does not work since it requires that all requests are known beforehand. This wont work in my case since the number of requests and the url of each request is dynamic and not known.

The number of pages/requests is inferred from the result of the previous request. So if the previous request returns anything the next page is requested to see if there are any more items.

I tried the following

this.items = [];
_fetch(url, page) {
    this.http.get(`${url}&pageSize=2&page=${page}`).subscribe(response => {
    this.items.concat(response.json().items)
    // if the list of items is not empty fetch another page
    if (response.json().items.length > 0) {
      this._fetch(url, ++page);
    }
  });
  return Observable.of(this.items);
}

However this.items is returned before the requests have a chance to complete so it is always returned as an empty list.

any ideas?

Ben Glasser
  • 3,216
  • 3
  • 24
  • 41

2 Answers2

2

You can use flatMap to chain the observables together

fetch(url){
    return this.http.get(`${url}/initial-api/`)
        .flatMap(res => {
            let page = res.page

            return this.get(`${url}&pageSize=2&page=${page}`);
        });
}

Now when you subscribe to the observable sequence, it'll chain the apis together

Here is a good read on flatMap/mergeMap: https://coryrylan.com/blog/angular-multiple-http-requests-with-rxjs

What is nice about flatMap is it keeps things in a single stream, as opposed to nested subscriptions which can get pretty messy depending on your code.

LLai
  • 13,128
  • 3
  • 41
  • 45
0

Since .subscribe returns an observable, you can simply chain them. As long as there was no error, it'll execute the second one:

this.http.get('some url').subscribe( result => {
    if (result) {
        // good to go execute second call.
        this.http.get('other url').subscribe( result2 => {
            // do something with result 1 and result 2
        })
    } else {
        // handle error. don't execute second call
    }
}, error => {
    // error occured. don't execute second call
})

You could also just setup promises in your app like so:

firstHttpCall() {
    return new Promise( (resolve, reject) => {
        this.http.get('someurl').subscribe( result => {
            result(result);
        }, error => {
            reject(error);
        });
    });
}
secondHttpCall() {
    return new Promise( (resolve, reject) => {
        this.firstHttpCall().then( result => {
            resolve(result);
        }, error => {
            reject(error);
        });
    });
}
someOtherCall() {
    // some stuff...
    this.secondHttpCall().then( result => {
        // everything went well.
    }, error => {
        // something went wrong
    })
}

If you could get to the point where you weren't dependent on the first calls value being used in the second call, I would say Promise.all is a viable option as well.

mwilson
  • 12,295
  • 7
  • 55
  • 95