The rapid execution of code causing an observable race condition

When exporting a CSV file in my code, I encounter a race condition while trying to modify some data before the export. The issue is that the id gets set correctly, but the number only updates after the method is called a second time. I believe the problem lies in the fact that the return statement is being executed before the observable has finished. How can I resolve this issue?

  public exportAsCSV(
    arr: ArrEntity[],
    origin: string,
    id?: string,
  ): Observable<Blob> {
    let headers = new HttpHeaders();

    if (arr && arr.length > 0) {
      headers = headers.append(
        xxx
      );

      if (origin === 'table') {
        this.getValues(3).subscribe(() => {
        for (const a of arr) {
          a.id = id; // works fine

          a.number = this.getNumberById(a.number); // only correct after 2nd call
         }
        });
      }

      return this.http.post(
        url,
        {
          chargeLogEntities: arr,
        },
        {
          headers: headers,
          responseType: 'blob',
        },
      );
    } else {
      return Observable.of();
    }
  }

Answer №1

To ensure proper execution of your code, it is crucial to avoid mixing synchronous and asynchronous operations. If your logic requires data that is fetched asynchronously, make sure to handle the flow correctly by waiting for the data before proceeding. This can be done through callbacks, Promises with `.then`, or Observables with `.subscribe`.

Another approach is to chain multiple Observables using operators like `flatMap/switchMap/concatMap` to maintain a smooth sequence of operations.

Your revised code snippet may look something like this:

public exportCSV(
    array: Entity[],
    source: string,
    identifier?: string,
  ): Observable<Blob> {
    let headers = new HttpHeaders();

    if (array && array.length > 0) {
      headers = headers.append(
        xxx
      );
      
      let prerequisites$ = of();

      if (source === 'table') {
        prerequisites$ = this.retrieveValues(3).pipe(
           tap(response => {
              for (const item of array) {
                 item.id = id;
                 item.number = this.fetchNumberById(item.number);
                 // Avoid mutating objects/arrays directly, consider returning modified data for further usage
              }
           })
        )
      }

      return prerequisites$.pipe(
         switchMapTo(this.http.post(
           url,
           {
             entities: array,
           },
           {
             headers: headers,
             responseType: 'blob',
           },
         )
      )
    } else {
      return Observable.of();
    }
  }

If you prefer not to mutate the original array, an alternative solution could be implemented as follows:

public exportCSV(
    array: Entity[],
    source: string,
    identifier?: string,
  ): Observable<Blob> {
    let headers = new HttpHeaders();

    if (array && array.length > 0) {
      headers = headers.append(
        xxx
      );

      let updatedArray$ = of(array);

      if (source === 'table') {
        updatedArray$ = this.retrieveValues(3).pipe(
           map(response => {
              return array.map(item => {...item, id: identifier, number: this.fetchNumberById(item.number)}
           })
        )
      }

      return updatedArray$.pipe(
         switchMap(updatedArray => this.http.post(
           url,
           {
             entities: updatedArray,
           },
           {
             headers: headers,
             responseType: 'blob',
           },
         )
      )
    } else {
      return Observable.of();
    }
  }

Utilizing `.pipe(switchMapTo())` or `.pipe(switchMap())` ensures sequential execution of the code.

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

Using TypeScript to Return a Derived Class as a Method's Return Type

I'm currently facing a challenge with an abstract class in typescript that includes a method to provide a callback for later use. The issue lies in the return type of the method, as it is set to the class itself, preventing me from using fluent style ...

Incorporating a component specified in a .jsx file into a TypeScript file

We recently acquired a react theme for our web application, but ran into issues transpiling the components. After resolving that problem, we are now facing type-related challenges. It seems that TypeScript is struggling because the props do not have a def ...

Converting a string to Time format using JavaScript

I am working with a time format of 2h 34m 22s which I need to parse as 02:34:22. Currently, I am achieving this using the following code: const splitterArray = '2h 34m 22s'.split(' '); let h = '00', m = '00', s = &a ...

Problem with Angular Slider

I'm in the process of creating a carousel component in Angular, but I'm facing an issue where the carousel is not appearing as it should. Below is the code for my carousel component. carousel.component.html: <div class="carousel"> ...

Executing a service request in Angular 2 using a versatile function

I have a function that determines which service to call and a function template for calling the service returned by that function. This function makes HTTP requests using http.get/http.post, which return an Observable and then perform a map operation on th ...

The type 'Dispatch<SetStateAction<boolean>>' cannot be assigned to type 'boolean'

Currently, I am attempting to transfer a boolean value received from an onChange function to a state variable. let [toggleCheck, setToggleCheck] =useState(false);` <input type="checkbox" id={"layout_toggle"} defaultChecked={toggleCh ...

Step-by-step guide on setting up cosmosDB databases and containers in azure functions with the node sdk

In my current setup, I have database initialization code that runs on every function request, impacting performance negatively. How can I verify the existence of a container in Cosmos DB using the node SDK? It's recommended to establish static conne ...

Exploring the zoom functionality and tooltip features of Area Range charts in Highcharts

I need help with a couple of things on the high charts area range chart: I am trying to enable zooming into the y-axis of the chart, but I am facing difficulties. Can anyone suggest how this can be achieved? Below is the link to my jsfiddle with the sam ...

Having trouble customizing the HTML range input?

I am looking to customize the appearance of a range input slider. Specifically, I want the slider to be green for the lower portion (where the thumb has moved) and grey for the remaining section. I have successfully changed the default styles from blue and ...

The name 'XXX' is nowhere to be found

I encountered an error stating "Cannot find name 'Calendar Component'" while attempting to add a route to a component from another module in my app.module.ts file. Below is the content of my app.module.ts file: // Importing Modules // import {B ...

Stopping HTTP client calls in OnDestroy hook of an Angular Service

Is it possible to automatically unsubscribe from an http call in an Angular service using the ngOnDestroy hook? Just to note, I am already familiar with using the rxjs 'take' operator or manually unsubscribing from the service within the compone ...

The method to create a global generic class in TypeScript

Is there a way to globally expose the Hash class? Access Playground here export {} class Hash<K, V> { } declare global { // How can we achieve this? } window.Hash = Hash // Making it globally accessible ...

The value of Angular Input remains unchanged within a FormArray

I am experiencing an issue with the Sequence No in my PreprocessingForm's FormArray. When I add a new row, the Sequence No does not change as expected. <tr class="mat-row" *ngFor="let dynamic of PreprocessingForm.controls.arithmeticI ...

Angular and the feature of contenteditable

I've been searching for a solution online, but I haven't been able to find any clear way to work with contenteditable events in Angular 6/7. It seems like Angular has a complicated approach to this issue and the feature doesn't appear to be ...

Prevent clicking outside the bootstrap modal in Angular 4 from closing the modal

Just starting out with angular4 and incorporating bootstrap modal into my project. I want the modal to close when clicking outside of it. Here's the code snippet: //in html <div bsModal #noticeModal="bs-modal" class="modal fade" tabindex="-1" rol ...

CompositeAPI: Referencing HTML Object Template - Error TS2339 and TS2533 when using .value to access Proxy Object

Having trouble referencing an element in VueJS 3 CompositeAPI. In my current implementation, it looks like this: <div ref="myIdentifier"></div> setup() { const myIdentifier = ref(null); onMounted(() => { console.log(myIden ...

Tips for personalizing Ion text area in Ionic?

Seeking assistance on how to effectively utilize ion-textarea. As a novice in the realm of Ionic, I am encountering various challenges while working with this feature. The issue lies in the fact that instead of displaying a scrollbar on the right side, the ...

Angular: utilizing two ngFors to target and update specific sections of the DOM

I'm facing a challenge and unsure if it's achievable in Angular. Below is a simple HTML code snippet: <div > <div *ngFor="let item of apiNames, let i = index" class="rescontainer"> <div class="resbox headline"> ...

Is there a more effective alternative to using the ternary condition operator for extended periods of time?

Do you know of a more efficient solution to handle a situation like this? <tr [style.background]="level == 'ALARM' ? 'violet' : level == 'ERROR' ? 'orange' : level == 'WARNING' ? 'yellow' ...

Testing the function that relies on a service through a unit test

I'm currently working on unit testing a component. However, I encountered an issue with one of its methods that utilizes a service and is causing a 'cannot read property 'then' of undefined' error. While I understand how to call a ...