Waiting for asynchronous subscriptions with RxJS Subjects in TypeScript is essential for handling data streams efficiently

Imagine having two completely separate sections of code in two unrelated classes that are both listening to the same Observable from a service class.

class MyService {
  private readonly subject = new Subject<any>();

  public observe(): Observable<any> {
    return this.subject.pipe();
  }
}

class A {
  constructor(private readonly service: MyService) {
    service.observe().subscribe( async (value) => { 
      await this.awaitAnOperation(value);
      console.log('Subscription complete for Class A', value);
    });
  }
}

class B {
  constructor(private readonly service: MyService) {
    service.observe().subscribe( (value) => console.log('Subscription complete for Class B', value));
  }
}

The current issue is that when the service triggers an event, the log from class B appears before A, even though A was subscribed first. The desired outcome is for all operations to run and complete before moving on to the next one. Although running synchronously would resolve the problem, A requires an async operation and B can only log after A has finished logging.

A and B have no knowledge of each other and should remain independent. In languages like C#, we can make an async method execute synchronously using GetAwaiter().Wait(); without it being discouraged, especially when it must run on the main thread. It would be helpful to have a similar option in TypeScript/JavaScript.

EDIT A subscribes prior to B. It's crucial that they also execute in the order they were subscribed. While this behavior is typically followed by default, executing a subscription on a different thread could cause the main thread to move on to the next subscription prematurely. This is a scenario that needs to be avoided somehow.

Answer №1

Dealing with a similar issue, I devised an operator called forkConcat to streamline the process. Instead of subscribing multiple times, I created a chain of operators where each one completes before the next begins. This approach ensured that all operations completed before moving on to the next value from the source.

The code implementation looked like this...

source$.pipe(
  forkConcat(
    concat,
    operatorA,
    operatorB,
    operatorC
) )

In this setup, forkConcat is defined as follows:

import { merge, Observable, of } from 'rxjs';
import { concatMap, Operator } from 'rxjs/operators;

// Definition and explanation of forkConcat function...

Although this approach worked, I eventually decided to discard it.

I threw it away

Upon further reflection, I realized that the need for forkConcat indicated a larger issue in my coding approach. Transitioning away from global variables, I reconsidered my design strategies based on subjects and observables instead. By passing values through a cleaner pipeline, I was able to achieve more clarity in my code structure.

In the end, I restructured my code to utilize a State interface, passing both the source values and state objects through the operators. This eliminated the reliance on global variables entirely, resulting in more robust error handling and improved code organization. Embracing this new methodology allowed for a more systematic and efficient workflow.

However, if you find value in the forkConcat method, feel free to leverage it within your codebase.

Answer №2

To achieve this, you can merge the two Observables using the switchMap operator from rxjs. This ensures that Observable B will not begin until Observable A has completed.

For a detailed example demonstrating this concept, refer to the section titled "Combining Observables in series" in the following link:

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

Creating a PEG Grammar that can match either a space-separated or comma-separated list

I am currently working on creating a basic PEG (pegjs) grammar to analyze either a space separated list or a comma separated list of numbers. However, it seems like I am overlooking a crucial element in my implementation. Essentially, I aim to identify pat ...

In TypeScript, the NonNullable type is like Required, but it ensures that all object properties are converted to non-

When working with TypeScript, you may have come across the Required type which transforms object properties into defined ones. For instance: interface Person { name?: string; age?: number; } Using Required<Person> will result in: interface Pe ...

Tips on how to navigate to the end of a div that has been created through ng-repeat in Angular JS with the

Here is the template code I am working with: <div class="chatbox" id="mailBody" > <div class="panel-body" ng-repeat="mail in mails"> <div class="m-b-none" ng-if="mail.inboxMessageType == 1"> <a href class="pull-left ...

Incorporating Vuetify into a Vue CLI application with the help of webpack

I am facing an issue with my Vue CLI application that uses Webpack and Vuetify. The Vuetify components are not loading properly, and I keep getting the following warnings: Unknown custom element: < v-app > - did you register the component correctly? ...

Utilize the useRef hook to dynamically retrieve the updated height when children are altered

I am working with an accordion component and using the useRef hook to measure the height of the children. However, I noticed that when I update the content of the children dynamically, the height measurement does not get updated unless I click on the toggl ...

CKEditor5: Unable to access the 'pluginName' property because it is undefined

I am facing a challenge in creating a custom image plugin for CKEditor that can seamlessly integrate with my own image upload system. While trying to set up this plugin, I encountered some difficulties. Oddly enough, the "out-of-the-box" plugins work perfe ...

When converting a PDF to a PNG, the precious data often disappears in the process

I am currently facing a problem with the conversion of PDF to PNG images for my application. I am utilizing the pdfjs-dist library and NodeCanvasFactory functionality, but encountering data loss post-conversion. class NodeCanvasFactory { create(w, h) { ...

What strategies can be employed to maintain reliable datetime management for a reservation system operating in diverse time zones?

Looking at the big picture: An interesting scenario arises when a hotel owner specifies a time frame for booking reservations at a restaurant (5pm - 10pm). Along with this information, there is also a timezone provided to ensure that dates are displayed i ...

Locate specific phrases within the text and conceal the corresponding lines

I have a JavaScript function that loops through each line. I want it to search for specific text on each line and hide the entire line if it contains that text. For example: <input id="search" type="button" value="Run" /> <textarea id ...

Establishing a connection between a Google spreadsheet to create and automatically update calendar events

I'm currently working on connecting my Google Sheet to a calendar so that it can automatically generate calendar events and keep them updated based on changes made in the sheet. The Google Sheet I'm using tracks new building opening dates and con ...

Navigating through two nested arrays in JavaScript to access an object

Having difficulty extracting the nested car values using JavaScript (lodash). Take a look at the JSON data below: { "cars":[ { "nestedCars":[ { "car":"Truck", "color" ...

Invoking PHP code from within Javascript will output the function as a direct string

I seem to be going in circles and missing something silly... My setup involves using CodeIgniter on the server-side and Bootstrap on the client, but that's not really the issue here... I am attempting to access a PHP value within a JavaScript functi ...

Getting into a dynamic named property inside another object in angular can be achieved by utilizing bracket notation

I encountered an issue in my Angular 8 project where I create an object from a JSON, but there is a dynamic property whose name is unknown until runtime. This causes problems when trying to access the value of that dynamic property within another object, l ...

Nonspecific _POST parameter error encountered while utilizing XMLHttpRequest()

I am currently experimenting with Ajax to add data into a database using the POST method. However, I am facing an issue where PHP is unable to recognize the post variables being sent. ajax: function createUser() { var _id=document.getElementById('ne ...

Issue with Angular binding not updating after being assigned in promise.then() function

Within my angular application, I have a $scope variable labeled as user which contains a name property. Whenever I click on a link to set this user variable to "test": <a href="#" ng-click="setUser()">Set User</a> Using the following functio ...

Unable to update the color of material icon using document.getElementById(item) method

if (document.getElementById(item).style.color == "grey") { document.getElementById(item).style.color = "red"; } <i class="material-icons" [ngStyle]="post.isLiked != null ? {'color': 'red'}: {'color': 'grey'}" id ...

Tips for retrieving the distance from the top of a specific div element and transferring it to another div

How can I add margin-top based on the tab that is clicked? Specifically, when TAB 4 is selected, I want the content to remain in the same position from the top. https://i.sstatic.net/ObDUg.jpg ...

"Utilizing the power of Twitter Bootstrap and Snap.js for

I am currently working on integrating Snap.js by jakiestfu with Twitter Bootstrap. I have managed to make it work to some extent (I can slide the content and open a drawer through a click event). However, I am facing a challenge where the drawer content re ...

Clearing error messages from a form using the reset button or after cancelling the form

I am having trouble removing the error outline around the input box and error messages displayed below it. When I cancel the form or click on the reset button, the input fields' content along with the error messages should be cleared. However, current ...

How can I activate TypeScript interface IntelliSense for React projects in VSCode?

Did you know that there are TypeScript interfaces designed for DOM, ES5, and other JavaScript modules? I am curious if it is feasible to have intellisense similar to the one provided by TypeScript Playground for various interfaces in a React project. ...