Is there a method in RXJS that allows an operator to pause and wait for a subscription to finish before proceeding with the workflow?

Being new to the world of RXJS, I have spent a considerable amount of time researching and seeking solutions on stackoverflow and various documentation sources before turning to ask for help here. However, despite my efforts, I am struggling to make my logic function correctly.

My current setup involves an Observable that fetches a collection of documents and makes use of the pipe operator to perform some transformations, such as utilizing the map operator to modify the objects. Everything seems to be working fine up to this point.

The issue arises when I need to send an "http request" for each document in order to retrieve specific data (referred to as "tags"). This http request is also treated as an Observable and requires subscription to fetch the necessary data. The challenge lies in the fact that the subscription process takes some time, resulting in the final object missing the required data.

let myFunction.pipe(
      // mapping to add missing data needed for the front-end
      map((results) => ({
        ...results,
        documents: results._embedded.documents.map((document) => ({
          ...document,
          tags: []
        })),
      })),
// mapping to loop through each document, and use the observable to get the tags with the document id
      map((results) => {

        let documents = results.documents.map((document: Document) => {
          // get Tags for each document
          let tagsToReturn = []
          this.getDocumentTags(document.id)
            .pipe(
       // map function to return only the ids for each document, and not the complete tag object
              map((tagsArray) => {
                const modifiedTagsArray = tagsArray.map((tagObject: any) => {
                  if (tagObject !== undefined) {
                    return tagObject.id
                  }
                })
                return modifiedTagsArray
              })
            )
              // the subscription to "actually" get the tags
            .subscribe((tagsArray: number[]) => {
              // Here the tags are found, but the latter code is executed first
              // document.tags = tagsArray
              tagsToReturn = tagsArray
            })

          // console.log(JSON.stringify(document))
          // Here the tags are not found yet
          console.log(JSON.stringify(tagsToReturn))

          return { ...document, tags: tagsToReturn }
        })

       // I then, normally return the new documents with the tags for each document, but it is empty because the subscribe didn't return yet.
        return {
          _links: results._links,
          page: results.page,
          documents: documents,
        }
      }),
      map((results) => {
        results.documents.forEach((doc) => {
          return this.addObservablesToDocument(doc)
        })
        return results
      })
    )

I have experimented with different approaches using operators like switchmap, forkjoin, concat, etc., but I have been unsuccessful in finding the correct solution. Therefore, I am reaching out to inquire if there is a way to resolve or manage this problem effectively.

In attempting to address the issue, I have explored options involving operators such as mergemap, concat, switchmap to transition to the new request, but encountered challenges in maintaining the global object.

I have tried to replicate/rework aspects of this approach

Answer №1

By utilizing the combination of mergemap and forkjoin, I successfully replicated the desired outcome.

Explaining this may be a bit challenging, as I am not an expert in Rxjs myself. However, I implemented code from a StackOverflow answer that I adapted to achieve this.

From my understanding, when using mergeMap in the pipe flow, it ensures that everything returned will be executed by the calling "subscribe()". Additionally, the mergeMap returns a forkJoin observable for each document tag.

I hope this explanation proves helpful.

.pipe(
      // mapping to add missing data needed for the front-end
      map((results) => ({
        ...results,
        documents: results._embedded.documents.map((document) => ({
          ...document,
          tags: []
        })),
      })),

  /******** Added Code *********/
      mergeMap((result: ResultsNew<Document>) => {
        let allTags = result._embedded.documents.map((document) =>
          this.getDocumentTags(document.id).pipe(
            map((tagsArray) => tagsArray.map((tagObject: any) => tagObject.id))
          )
        )
        return forkJoin(...allTags).pipe(
          map((idDataArray) => {
            result._embedded.documents.forEach((eachDocument, index) => {
              eachDocument.tags = idDataArray[index]
            })
            
            return {
              page: result.page,
              _links: result._links,
              documents: result._embedded.documents,
            }
          })
        )
      }),
        /******** Added Code *********/

      map((results) => {
        results.documents.forEach((doc) => {
          return this.addObservablesToDocument(doc)
        })
        return results
      })
    )

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

Getting data for a single post in Nextjs using the getServerSideProps function

As I work on developing a news website with Nextjs + Strapi, the challenge of maintaining high dynamicity and real-time updates poses obstacles in utilizing the getStaticProps method. Even incremental static regeneration falls short for this task. Addition ...

What is the best way to display a progress bar as a percentage?

Is there a way to visually represent the progress of an upload on the screen using percentages? I want to display a bar that starts at 0% and updates with the percentage from the percentComplete variable. Once the upload is finished, I'd like to show ...

Tips for combining several fields with rowspan grouping in a primeng table

Utilizing the Primeng data table for grouping rows and columns has been quite helpful. However, I have encountered a limitation where I can only group one field at a time based on the example provided on the official site. Here is the HTML code snippet th ...

Delivering a static Angular app through an Express Server

In my current setup, I have an Angular app being served as static content in an Express Server. When Express serves static files, it automatically adds an ETag to them. Subsequent requests will then check if the ETag matches before sending the files agai ...

Creating seamless compatibility between the elliptic library in JavaScript and the ecdsa library in Golang for efficient cross-platform operations

I am having issues with validating a signature created using the elliptic JavaScript library and the ecdsa library from Golang. The elliptic curve in question is secp256k1. Below are some snippets of code: Here are the TypeScript utility functions: impor ...

Using Observable<any> as an input argument in a child component

When a button is clicked, a different Observable is loaded. In the main component, I have this : <button (click)="onClickMe1()">Click me 1!</button> <button (click)="onClickMe2()">Click me 2!</button> However, nothing appears in ...

Tips for extracting a substring from a string in JavaScript or ReactJS

Is there a way to extract a specific string from a website URL in ReactJS? For example, if the URL is https://www.mrkashyap.com/users/manish, I only want to retrieve the word manish or anything after users/. I currently have the URL stored in a variable ...

The compatibility between Babel 7 and the preset-es2015 is not very reliable

After reading this useful tutorial on implementing server-side rendering with create-react-app, I attempted to execute the following code snippet: require('ignore-styles'); require('babel-register')({ ignore: [/(node_modules)/], ...

Find folders with names greater than X using node

Looking to retrieve folders with names greater than 1.0.0 using node.js Can anyone guide me on how to accomplish this with the provided directory structure? |---version |--1.0.0 |--1.2.0 |--0.9.0 Appreciate any help, as I am still new to wor ...

Exploring the HashLocationStrategy feature in Angular 18

Looking to integrate HashLocationStrategy in Angular 18, but facing the challenge of not having an App Module in this version ...

The error message "Uncaught TypeError: Unable to retrieve the 'lat' property of an undefined object when serializing leaflet data using $.param()" is displayed

Being a complete novice in JavaScript, I am experimenting with posting user location and map bounds using Leaflet and an AJAX call. While my event handler stateUpdater.onLocationFound successfully logs the user coordinates and map bounds, I encounter an is ...

The onsubmit function is not functioning correctly in combination with Ajax

My current implementation involves utilizing Ajax and Php for user login validation. Unfortunately, the form onsubmit function is not properly functioning. <form action="loggedin.php" onsubmit="return test()" method="post"> Below is the correspondi ...

What is the process for displaying a Bootstrap Icon on a webpage?

I recently started using Bootstrap and I'm in the process of creating a login screen for my app. I want to incorporate MDBIcons for Google, Twitter, and Facebook. I referred to the documentation provided at However, upon previewing my webpage, I enco ...

Merging two divs in AngularJS

Currently in the process of converting an application from jQuery to AngularJS, I'm faced with a challenge involving combining two twin beds into a king bed using a bootstrap button toggle. Let's simplify this scenario by imagining two rounded bo ...

Angular function implementing a promise with a return statement and using the then method

I have a function in which I need to return an empty string twice (see return ''. When I use catch error, it is functioning properly. However, I am struggling to modify the function so that the catch error is no longer needed. This is my current ...

The variable "vue" is not properly defined within the instance, yet it is being called

I'm currently working on a Vue app and encountering an issue. The onScroll function is working correctly, but when I click the button component to trigger the sayHello function, I receive an error message. The error states: "Property or method &apo ...

What is the process for accessing the theme spacing unit in MUI 5?

In previous iterations of MUI, accessing the theme spacing unit was possible through theme.spacing.unit, with a default output of 8. However, this property has been removed in MUI 5. I am having trouble finding documentation on how to access the theme sp ...

Dynamic Angular jQuery Datatable with ngModel binding for interactive data population and server-side filtering or processing - handle errors for mData property

Looking for a way to configure an Angular jQuery datatable to send dynamic parameters to the server? The data needs to be handled on the server side. These parameters are linked to ngModel and should trigger a "onChange" event, causing the datatable to u ...

Website loses its css after printing

I'm having trouble printing a website with a simple layout featuring two columns, each containing tables. The website I want to print is located at the following link: . However, when I try to print it using the JavaScript function window.print(), the ...

Creating API data by subscribing to the global Angular 5

What is the best way to ensure that the author variable is accessible in all methods? I have an array called author[] which is assigned data from an API in the subscribe method. Although I can bind this property to an HTML component, it appears as an empt ...