Search code examples
angularrxjsangular-httpclient

Preserve the order of the http calls in angular


I'm trying to solve the order problem I'm facing with several approaches that I found here on SO without a success.

I have a method, where I'm loading some data for an array of leaflet layers:

private loadSelectedTileLayersCapabilities(): void {

        let tempTileLayer;

        this.selectedTileLayerIds.forEach(
            (selectedTileLayer: string) => {
                tempTileLayer = this.getTileLayerById(selectedTileLayer);

                this.capabilitiesService.getTileLayerDimensions(tempTileLayer.url, tempTileLayer.name, tempTileLayer.id)
                .subscribe(
                    dimensions => this.displayNewTileLayer(dimensions)
                );
            }
        );
    }

And then I have a method, where the http call is happening:

public getTileLayerDimensions(urlToFormat: string, tileLayerName: string, tileLayerId: string): Observable<Dimensions> {

        const capabilitiesUrl = `serviceUrl`;

        return this.httpClient.get(capabilitiesUrl, {responseType: "text"})
            .map(res => {

                // Doing stuff with data

                return dataForLayer;
            });

    }

The problem is, the displayNewTileLayer(dimensions) method is called in random order. Is there a way to preserve the order in which the items were stored in selectedTileLayerIds array?


Solution

  • Since the http-calls are asynchronous, the responses may not arrive in the same order the requests were made. What you could do is to create a list of requests, create a forkJoin and wait for all responses to resolve. You can then call the displayNewTileLayer(dimensions)-method for all responses.

    Here is an example

        const httpCalls = []; // store the requests here
        for (let i = 0; i < 3; i++) {
          httpCalls.push(this.http.get('someUrl').map(response => response));
        }
        forkJoin(httpCalls).subscribe(res => {
          // all responses completed. returns an array of data (one for each response).
          console.log('res', res);
        });
    

    In you case, this code may work: (code not tested, and you may have to import the forkJoin operator in your code)

    import { forkJoin } from 'rxjs/observable/forkJoin';

    private loadSelectedTileLayersCapabilities(): void {
    
        let tempTileLayer;
        let requests = []:
    
        this.selectedTileLayerIds.forEach(
            (selectedTileLayer: string) => {
                tempTileLayer = this.getTileLayerById(selectedTileLayer);
                const request = this.capabilitiesService.getTileLayerDimensions(tempTileLayer.url, tempTileLayer.name, tempTileLayer.id)
                requests.push(request);
    
            }
        );
        forkJoin(requests).subscribe(res => {
          res.forEach(dimension => this.displayNewTileLayer(dimension));
        })
    }