Tips for serializing the execution of an array of observables

I am currently working on a validation process that goes through data in a table row by row. Due to the fact that each row validation requires access to a shared resource, it is crucial that the access to this resource is serialized.

public validate():Observable<boolean>{
    const rowValidations:Observable<boolean>[] = dataRows.map(row=>this.validateSingleRow(row);
    return forkJoin(...rowValidations).pipe(
      map(results=>results.every(r=>r))
    )
}

My understanding is that forkJoin does not wait for each observable to finish before moving on to the next one, unlike concat, so this approach may fail. On the other hand, concat serializes all observables into a single stream.

Is there a way to achieve the subscription order of concat while still obtaining an array of results from each observable like what forkJoin provides, effectively synchronizing the execution of each inner observable (similar to Java's synchronized validateSingleRow)?

Answer №1

If you are certain that each this.validateSingleRow(row) will emit only once, utilizing toArray() can simplify the process:

concat(...rowValidations).pipe(
  toArray(),
);

The use of concat ensures the correct order, while toArray() gathers all emissions into a single array and emits it after the source Observable completes.

However, in scenarios where validateSingleRow may emit multiple times and you only need its latest value, employing scan might be more appropriate:

const indexedRowValidations = rowValidations.map((o, index) => o.pipe(
  map(result => [index, result]),
));

concat(...indexedRowValidations ).pipe(
  scan((acc, [index, result]) => {
    acc[index] = result;
    return acc;
  }, {}),
  takeLast(1),
);

(I haven't tested this code, but I believe you grasp the concept :)).

Answer №2

Would this code snippet serve your needs?

class ValidationClass {
  dataRows = [1, 2, 3];

  public checkValidity(): Observable<boolean[]> {
    return this.processSequentially(this.dataRows);
  }

  private processSequentially<T>([current, ...remaining]: T[]): Observable<boolean[]> {
    return current
      ? this.validateSingleRow(current).pipe(
          switchMap((result) =>
            this.processSequentially(remaining).pipe(map((array) => [result, ...array]))
          )
        )
      : of([]);
  }

  // Mock function for validation
  private validateSingleRow(currentValue: any) {
    console.log(`Validating ${currentValue}...`);
    return of(Math.floor(Math.random() * 2) === 1).pipe(
      delay(1000),
      tap((result) => console.log(`Validation Result: ${result}`))
    );
  }
}

const instance = new ValidationClass();

instance.checkValidity().subscribe(console.log);

Check out the StackBlitz demo here

Answer №3

The solution I came up with to meet my requirements may seem simpler than expected. By using the concat function along with toArray(), the code looks like this:

const rowDataValidation:Observable<boolean>[] = allRows.map(row=>defer(()=>this.validateSingleRow(row)));
return concat(...rowDataValidation).pipe(
  toArray(),
  map(results=>results.every(result=>result))
)

This allows each row to be validated one at a time and then transforms the boolean stream into an array of booleans using the toArray function.

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

Preserving the background image on an html canvas along with the drawing on the canvas

Can users save both their drawings and the background image after completing them? var canvas = document.getElementById("canvas"); // This element is from the HTML var context = canvas.getContext("2d"); // Retrieve the canvas context canvas.style.ba ...

Creating Stylish Popovers in Flask with Bootstrap 5 (Using Jinja Templating)

Currently, I am attempting to implement a simple popover feature using a div component within a Jinja template that will be rendered by Flask. Despite my efforts, I am encountering difficulties in getting it to function correctly. Within my 'layout.j ...

Is there a solution for the error message "Operator '+' cannot be used with types 'string | number' and 'string' | number'"?

Here's the scenario: I'm encountering an issue where I am invoking a function within another function. This inner function has the capability to return either a string or a number, and my goal is to combine that with another value. However, I kee ...

The function Router.replace within Next Js is not recognized

When attempting to use Router.replace() for replacement, I encounter the error message: TypeError: next_router__WEBPACK_IMPORTED_MODULE_6__.Router.replace is not a function https://i.sstatic.net/Ldmca.png I have attempted to do it in this way: import { ...

Error message: Three JS is unable to locate the property 'add' due to its undefined status

Currently engaged in a project where a script fetches both a .svg and a .obj file upon clicking a button. The button successfully locates the files, but then encounters an error: Uncaught TypeError: Cannot read property 'add' of undefined Below ...

The request to register at http://localhost:3000/api/auth/register returned a 404 error, indicating that the

I've successfully set up a backend for user registration authentication, which works fine in Postman as I am able to add users to the MongoDB. However, when trying to implement actual authentication on localhost:3000/frontend, I encounter an error dis ...

Upgrading to Angular 18 Leads to Issue with Spread Operator: 'TypeError: res is not iterable' Error

Following the migration of my Angular project from version 9 to version 18, a new error has surfaced: ERROR TypeError: res is not iterable onEscalate details.component.ts:735 RxJS 5 next _next next next error ...

Problems encountered while invoking a function using ng-click within an AngularJS controller?

You can check out the code by visiting When attempting to login, I trigger the simple login() function of the AuthController using ng-click on the login form button. However, upon calling this function, I encounter an error message in the console that I a ...

Video background in webflow not currently randomizing

I want to add a dynamic video background to my website created with Webflow. I attempted to achieve this using Javascript by including the following links: "https://s3.amazonaws.com/webflow-prod-assets/63e4f3713963c5649a7bb382/63f787b42f81b8648e70fed ...

Position the div in the center of a container that is 100% width and has floating elements with dynamic widths

I am looking to align 3 divs inside a container div, with the arrangement of [LEFT] [CENTER] [RIGHT]. The container div should be 100% wide without a fixed width, and I want the center div to stay centered even when resizing the container. The left and ...

Commencing the invocation of actions from effects in NgRx

How can I trigger an action before or at the beginning of an effect in my code? For instance: saveSomething$ = createEffect(() => this.actions$.pipe( ofType(SaveProjectAction), tap(() => ImSavingNowAction()), // Triggering action her ...

In ReactJs, you can use break tags to format a lengthy string and also apply color to certain words within the string

When working with ReactJs, my objective is to format a lengthy string by inserting <br/>. I was able to achieve this using a JavaScript function as shown below: The long string in question: export const fadeAnimation = { \n initial: {opacity: ...

How to access an ejs variable within a Node.js environment using Javascript

As I experiment with my JavaScript code in an HTML file to interact with an ejs variable. Here is the Node.js code snippet: res.render('target.ejs', {data:user}); Everything works fine when I include this in my HTML file: <p> <h1> ...

What steps should I take to make the code in jsfiddle functional on my Visual Studio Code platform?

const canvasEle = document.getElementById('drawing-container'); const canvasPad = document.getElementById('pad'); const toolbar = document.getElementById('toolbar'); const context = canvasEle.getContext('2d'); const ...

Adjust Scale to Less than 1 in JVectorMap

I am in the process of developing a website that includes a full-size map using JVectorMap. The map currently occupies 100% of the width and height of the page, but I would like to add a border around it when fully zoomed out. However, when the map is zoom ...

Can triggering an ngrx effect lead to memory leakage?

Currently, I am delving into the world of NgRx and grappling with a concept that has been puzzling me. As I create an Effect and dispatch an action, the createEffect function comes into play. What throws me off is the dispatch configuration within createEf ...

Searching for repeated values across disparate object fields?

Inquiring about the method to utilize mongoose for identifying duplicate values across different fields. Providing a sample document for reference: { "followers": { { "_id": "5bf6d610d3a3f31a6c75a9f4" }, ...

Texture flagged for refresh, yet image remains undefined

I'm exploring the possibility of utilizing openlayers for map creation, and then leveraging the map's canvas as an input for threejs' CanvasTexture to generate a texture. However, I seem to be encountering an issue and am unsure how to addre ...

What causes JavaScript parseFloat to add additional value in a for loop implementation?

I am facing a challenge where I need to convert an array of strings into an array of decimal numbers. The original array of strings is structured like this: var array = [" 169.70", " 161.84", " 162.16", " 176.06", " 169.72", " 170.77", " 172.74", " ...

Having trouble displaying dynamically generated texture from a fragment shader in ThreeJS

I've been working on creating a texture using a Three.WebGLRenderTarget and then trying to access it in a fragment shader in the following stage. The concept is to execute the first stage once to generate a complex and costly SDF map, and then access ...