Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to run web request (Observable) in `finalize()` or achieve similar behavior?

I want to somehow chain two web requests such that:

  1. Request A (Observable<void>) is always run.
  2. Request B (also Observable<void>) is also always run, after A and even when A completes with errors (like finalize()).
  3. If A errors with EA, I want the whole pipeline to complete with EA.
  4. If A succeeds, but B errors with EB, I want the whole pipeline to complete with EB.
    • I could also do with the pipeline just succeeding in this case.
  5. If both error, then I want the whole pipeline to complete with EA.

This is the solution I could come up with, but it feels very clumsy:

// Changes different parts of backend state in multiple steps,
// some of which might fail.
const reqA: Observable<void> = this.mutateSystemState();

// Loads & renders the system state, updating the parts that
// could be changed.
const reqB: Observable<void> = this.updateSystemStateDisplay();

const pipeline = reqA.pipe(
    catchError(e => of({ errA: e })),
    mergeMap(resultA => reqB.pipe(
        catchError(e => of({ errB: e })),
        map(resultB => {
            if (resultA instanceof Object) {
                throw resultA.errA;
            } else if (resultB instanceof Object) {
                throw resultB.errB;
            }
        })
    ))
);
like image 662
Good Night Nerd Pride Avatar asked Oct 18 '25 03:10

Good Night Nerd Pride


2 Answers

The materialize operator converts next, error, or completion 'events' into ObservableNotification objects.

Assuming you want pipeline$ to either error or complete without emitting a value, you can do the following:

const pipeline$ = reqA.pipe(
  materialize(),
  concatMap(a => reqB.pipe(
    materialize(),
    map(
      // Both have executed.
      // Both a & b are ObservableNotification objects
      // Emit a if ErrorNotification
      b => a.kind === 'E' ? a
      // Otherwise emit CompleteNotification if b is a NextNotification
        : b.kind === 'N' ? {kind: 'C'}
      // Otherwise emit b
        : b
    )
  )),
  // At this point, we've got reqA's ErrorNotification, reqB's ErrorNotification or a CompleteNotification.
  // Either way, this observable is closing before the first materialize gets a chance to emit reqA's CompleteNotification
  dematerialize()
);
like image 161
JSmart523 Avatar answered Oct 20 '25 16:10

JSmart523


onErrorResumeNext()

whatever reqA$ error or not, will subscribe reqB$

reqA$.pipe(
  onErrorResumeNext(reqB$)
).subscribe(...);

https://stackblitz.com/edit/rxjs-ps5f1j


const pipeline$ = of(reqA$, reqB$).pipe(
  concatMap(materialize()),
  filter(({kind}) => kind === 'E'), // filter Error
  reduce(identity), // collect then return first value
  dematerialize(),
);

or

const pipeline$ = of(reqA$, reqB$).pipe(
  concatMap(materialize()),
  max(({kind}) => +(kind === 'E')),
  dematerialize(),
);

https://stackblitz.com/edit/rxjs-jnqhjw

like image 31
Eddy Lin Avatar answered Oct 20 '25 16:10

Eddy Lin



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!