4

I've got an API call that I need to make multiple times as the API doesn't support requests for multiple items. What I have written so far ends up in an endless loop of sending GET requests to the server. I believe I don't understand the nature of Observables in the below example. My assumption is that each call in the while loop subscribes to a new single object and that when it is resolved it will be placed in the array. How can I modify the below code to achieve what I want?

getSingle(): Observable<Single> {
    return this.http.get(this.url, this.options)
     .map((r: Response) => r.json().data as Single);
}

getMultiple(num: number): Single[] {
    let multiple: Single[] = [];

    let i = 0;
    while (i < num) {
        this.getSingle().subscribe(single => {
            multiple.push(single);
            console.log('Success');
            i++;
        }, err => {
            console.log('Failure');
        });
   }

   return multiple;
}

2 Answers 2

4

Using the while loop to make multiple asynchronous HTTP requests, and then subscribe to all of them separately should be avoided in order not to have many Observable connections opened. We can use Observable.forkJoin operator instead.

This is how the implementation should look like:

getSingle(singleUrl: string): Observable<Single> {
    return this.http.get(singleUrl, this.options)
        .map((r: Response) => r.json().data as Single);
};

getMultiple(): Observable<Array<Single>> {
    let singleUrls = ['singleUrl1', 'singleUrl2', 'singleUrl3']; // can be replaced with any 'Single' identifier

    let singleObservables = singleUrls.map((singleUrl: string, urlIndex: number) => {
        return this.getSingle(singleUrl)
            .map(single => single as Single)
            .catch((error: any) => {
                console.error('Error loading Single, singleUrl: ' + singleUrl, 'Error: ', error);
                return Observable.of(null); // In case error occurs, we need to return Observable, so the stream can continue
            });
    });

    return Observable.forkJoin(singleObservables);
};

this.getMultiple().subscribe(
    (singles: Array<Single>) => {
        console.log(singles); // [Single, Single, Single];
        // In case error occured e.g. for the 'singleUrl' (our 'Single' identifier) at position 1,
        // Output wil be: [Single, null, Single];
    }
);

Hope this helps!

Sign up to request clarification or add additional context in comments.

1 Comment

Consider using mergeDelayError instead of forkJoin with a "hackish" catch: It has the benefit that it won't squash the results into an array and when all requests have completed you will get a proper error callback in case you had any.
0

Ideally you would only subscribe once in the constructor since a subscribe block can be called many times and then use an RxJS subject and next() it inside the loop

However, a quick fix if you don't care too much is to:

this.getSingle().first().subscribe

you will need to import the first operator. This will ensure subscribe only is called once only. that way you can sub many times no worries.

I'm guessing the reason you are getting infinite HTTP request is that you failure block is being hit and you need to do ...

}, err => {
       i++;
        console.log('Failure');
 });

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.