Merge a series of Observables into a single Observable

I am facing a challenge where I need to merge the outcomes of various http calls into a single Observable. These calls must be executed sequentially, with each call waiting for the previous one to finish. The catch is that the number of http calls needed is not known until runtime.

While I have come across similar queries before, they always had a fixed number of initial calls, making it easy to manage them using nested or chained pipe operations. In my case, however, I need to handle anywhere from 0 to n API calls.

My initial thought is to use concatMap(), but I am not entirely sure and couldn't quite grasp the syntax required to implement it successfully.

// Submit all phones to the API individually and return the updated results in a single array.
savePhones(phones: Phone[]): Observable<Phone[]> {

    let updatedPhones: Phone[] = [];

    if (phones.length === 0) {
        return of<Phone[]>(updatedPhones);
    }

    // I need a solution like this but for handling multiple phones. 
    // Add each Observable result to the array after completion.
    // Begin the next Observable only after the previous one finishes.
    // Finally, return the Observable array once all are done. 
    return this.postPhone(contactId, phones[0])
        .pipe(
            map(response1 => {
                let p1 = new Phone();
                p1.loadFromJson(response1);
                updatedPhones.push(p1);
            }),
            concatMap(() => this.postPhone(contactId, phones[1])
                .pipe(map(response2 => {
                    let p2 = new Phone();
                    p2.loadFromJson(response2);
                    updatedPhones.push(p2);
                }))),
            concatMap(() => this.postPhone(contactId, phones[2])
                .pipe(map(response3 => {
                    let p3 = new Phone();
                    p3.loadFromJson(response3);
                    updatedPhones.push(p3);
                }))),
            map(() => {
                return updatedPhones;
            })
        );

}

private postPhone(
    contactId: string,
    phone: Phone): Observable<Phone> {

    let criteria = phone.toJson();
    let url = phone.isNew()
        ? `${apiUrl}/contacts/${contactId}/phones/add`
        : `${apiUrl}/contacts/${contactId}/phones/${phone.phoneId}/update`;

    return this.httpClient.post(url, criteria));
}

Your insight would be greatly appreciated.

Answer №1

Consider using forkJoin method

savePhones(phones: Phone[]): Observable<Phone[]> {
    return phones.length === 0 ? of<Phone[]>([]) : forkJoin(phones.map(
      phone => this.postPhone(contactId, phone).pipe(
        map(response => new Phone().loadFromJson(response)),
      )
    ));
}

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

The gltf model fails to load on Chrome for Android when using Threejs

I encountered an issue while attempting to showcase a basic GLTF 3d model on my website. Surprisingly, it functions smoothly on desktop Mac and Windows, as well as on iOS in Safari and Firefox. However, it fails to load on Android's Chrome browser. St ...

What is the best way to set up the typeRoots option for proper configuration

I have a unique yarn monorepo structure that is oddly shaped. Here's how it's set up: monorepo root ├── frontend │ ├── dashboard <-- not managed by yarn workspaces │ | ├── src │ | ├── node_modules │ ...

Disable caching in Nginx for a single-page HTML application

My single page app is set up using AngularJS and gulp. Whenever I run the command gulp build, it compiles my assets into minified JavaScript and CSS files like app98D9898.css. The random number in the file name changes with each build. The HTML page named ...

Using AngularJS to Send Elements to Scripts and Selectors

When I use the following template: <div id="container"> <textarea name="message"></textarea> <button value="send" ng-click="testMethod($parent)"> </div> In the JavaScript code: $scope.testMethod = function(element) ...

Transforming a unirest 'GET' call into an axios request

I am currently utilizing TheRundown API to access sports data. The example provided to me makes use of unirest, but I am endeavoring to adapt that example into an axios request. While I have managed to make it work for the most part, my main challenge lies ...

Convert an object to nested JSON in Angular 5

I am struggling with using Angular 5 HttpClient to send a post request because I am having trouble casting an object to nested JSON. For instance, I have the following class: export class Team { members: Person[]; constructor(members: Person[]) ...

What is the best way to add HTML content to a specific div element within an AJAX call's success function?

In the provided ajax script, my goal is to insert HTML content into a specific div element inside a table. The main div element in question is named "ajaxcom", but I need to insert the HTML content into a div element with the class = "divrep". How can I ac ...

Universal variable arguments

Is there a way to modify this function that accepts generic rest parameters to also accept an array parameter without using the spread operator? This would make chaining things much clearer: function fn<T>(...args: T[]): Something<T> { } let s ...

Is it possible to use the same identifier for both the name and id attributes in HTML?

In the world of coding, the "name" attribute is often used in server-side programming to send name/value pairs in requests. On the other hand, the "id" attribute is commonly utilized in client-side programming such as Javascript and CSS. However, both att ...

Generate a fresh array by evaluating the differences between two arrays of objects

Imagine having two arrays of objects like this: let oldBookDetails = [ {'name':'Harry pottar','amount':10, is_modified: false}, {'name':'LOTR','amount':20, is_modified: false}, {' ...

What is the role of the app.use method in ExpressJS in handling URL redirects that end with a "/"?

This script automatically redirects URLs that end with a "/" to the same URL without it. For example, if a user visits http://localhost:3000/about/, they will be directed to http://localhost:3000/about. This ensures that image URLs and other HTML file refe ...

Karma testing shows quick results, but in reality, the performance is sluggish

If you'd like a visual explanation, check out this video (or see the gif below): The Karma progress reporter may indicate that the tests are taking milliseconds, but in reality, it's taking much longer... I mentioned this on Twitter and was adv ...

What is the process of refreshing a page post-loading?

I'm working on a fundraising webpage and I want to display the live amount of money raised. Essentially, when one user donates while another is viewing the total amount raised, I want the displayed number to update in real-time. My backend setup incl ...

Issue with invoking controller action in MVC4 via AJAX

Below is the method in my controller: public JsonResult GetRights(string ROLE_ID) { var result = db.APP_RIGHTS_TO_ROLES.Where(r => r.FK_ROLE_ID.ToString() == ROLE_ID).Select(r => r.APP_RIGHTS).ToList(); return Json(re ...

What is the process for setting a specific version of Node for a project on my local machine?

I am currently facing an issue with setting up Node across multiple developers' machines for a project. The challenge lies in the fact that not all team members are experienced in Node or JavaScript, and we need to ensure that everyone has the correct ...

Utilizing Provide/Inject in Vue 3 Typescript to retrieve a method within a component, encountering the possibility of an 'undefined' Error Object

Having trouble calling the loginWithRedirect function within the header.vue component using inject feature in Vue 3 and Typescript. ERROR in src/components/global/HeaderMenu.vue:77:17 TS2339: Property 'loginWithRedirect' does not exist on type ...

scrolling through a list using .slice choosing an excessive amount of items

Is it possible to create a dynamic pager that can be customized using parameters from the URL? I have noticed that when I hardcode the perTime variable, everything works fine. However, as soon as I try to use a parameter from the URL, the page starts behav ...

Text displaying as actionable icons

In my React project, I am utilizing material-table (https://material-table.com/#/) and have successfully imported the necessary icons. However, when viewing the action bar, the icons are displaying as plain text instead of the Material Icon. import React, ...

Modify the information and return it to me

I am attempting to modify and return the content inside a custom directive (I have found some resources on SO but they only cover replacement). Here is an example: HTML <glossary categoryID="199">Hello and welcome to my site</glossary> JS . ...

Tips for removing data from documents that include automatically generated IDs

In my Angular project, I am utilizing google-cloud-firestore as the database. To import Firestore, I used the code import { AngularFirestore } from '@angular/fire/firestore';. The following function is used to add data to the database: changeLev ...