Boost Front‑End Reliability with Real‑World RxJS Anti‑Corruption Layer Patterns

This article demonstrates how to use RxJS Observable to build robust front‑end anti‑corruption layers, covering stability improvement, splash‑screen timing, automatic fast‑API selection, race‑condition handling, and high‑order data composition with practical code examples and online demos.

Alibaba Terminal Technology
Alibaba Terminal Technology
Alibaba Terminal Technology
Boost Front‑End Reliability with Real‑World RxJS Anti‑Corruption Layer Patterns

After publishing the article "Building Front‑End Anti‑Corruption Strategies with Observable", some readers questioned the simplicity of the examples, suggesting that the same problems could be solved with Promise and that introducing RxJS might increase complexity. While any Observable scenario can theoretically be implemented with Promise, the examples in this article illustrate how an Observable‑based anti‑corruption layer becomes valuable in more complex business situations.

Interface Stability Improvement

Assemble low‑success‑rate APIs into a high‑success‑rate API Online demo: https://stackblitz.com/edit/rxjs-stable-improvement Operators: retry / retryWhen / delay

We simulate an API with a 50% success rate and then use RxJS retry to boost its success probability to 99.9% by retrying up to ten times.

function unstableAPI(): Promise<boolean> {
  return new Promise((resolve, reject) => {
    if (Math.random() < 0.5) {
      resolve(true);
    } else {
      reject('error');
    }
  });
}

Using retry:

function stabilizedAPI(): Promise<boolean> {
  return lastValueFrom(
    from(defer(() => unstableAPI())).pipe(retry(10))
  );
}
Retry example
Retry example

To avoid rapid repeated retries that could cause an avalanche effect, we replace retry with retryWhen and add a 1‑second delay between attempts.

function stabilizedAPI(): Promise<boolean> {
  return lastValueFrom(
    from(defer(() => unstableAPI())).pipe(
      retryWhen(errors => errors.pipe(delay(1000), take(10)))
    )
  );
}
RetryWhen example
RetryWhen example

Interface Timing Adjustment

Provide a dedicated loading API for the splash screen Online demo: https://stackblitz.com/edit/rxjs-minimal-response-time Operators: forkJoin / delay

The splash screen display time should be the greater of the network delay and a minimal display duration to avoid flicker. The formula is:

Splash display time = Max(networkDelay, minimalDelay)

We simulate the network delay with setTimeout:

function initData(): Promise<{ name: string }> {
  return new Promise(resolve => {
    const networkDelay = Math.random() * 3000;
    setTimeout(() => {
      resolve({ name: 'lucy' });
    }, networkDelay);
  });
}

Using forkJoin and delay we ensure the splash API respects the minimal delay:

function initDataWithMinimalDelay(minimalDelay: number): Promise<{ name: string }> {
  return lastValueFrom(
    forkJoin([
      from(defer(() => initData())),
      of(true).pipe(delay(minimalDelay)),
    ]).pipe(map(([data]) => data))
  );
}
Splash timing example
Splash timing example

Interface Best‑Selection (Racing)

Automatically choose the faster API Online demo: https://stackblitz.com/edit/rxjs-race-query Operator: raceWith

We simulate a fast API (1 s) and a slow API (3 s) and use raceWith to obtain the first response.

function fastAPI(): Promise<string> {
  return new Promise(resolve => {
    setTimeout(() => resolve('fast data'), 1000);
  });
}
function slowAPI(): Promise<string> {
  return new Promise(resolve => {
    setTimeout(() => resolve('slow data'), 3000);
  });
}
function getFasterOne(): Promise<string> {
  return lastValueFrom(
    from(defer(() => fastAPI())).pipe(
      raceWith(from(defer(() => slowAPI())))
    )
  );
}
RaceWith example
RaceWith example

Race‑Condition Handling in the Anti‑Corruption Layer

Observable anti‑corruption layer provides built‑in race handling Online demo: https://stackblitz.com/edit/rxjs-race-condition Operators: exhaustMap / switchMap / concatMap

Using exhaustMap, only the first request is processed while subsequent clicks are ignored:

fromEvent(document.getElementById('button'), 'click')
  .pipe(exhaustMap(() => getData()))
ExhaustMap example
ExhaustMap example

With switchMap, only the latest request is kept, cancelling previous ones:

fromEvent(document.getElementById('button'), 'click')
  .pipe(switchMap(() => getData()))
SwitchMap example
SwitchMap example

Using concatMap, all requests are queued and executed sequentially:

fromEvent(document.getElementById('button'), 'click')
  .pipe(concatMap(() => getData()))
ConcatMap example
ConcatMap example

High‑Order Data Assembly

Abstract high‑order data requests into a single API Online demo: https://stackblitz.com/edit/rxjs-high-order-query Operators: mergeMap / map / forkJoin

We simulate two APIs: getList returns a list of items, and getStatus returns a status for a given item ID. Using mergeMap and forkJoin, we compose a single observable that returns the list with each item's status attached.

function getList(): Promise<Array<{ name: string; id: string }>> {
  return Promise.resolve([
    { name: 'John Brown', id: '1' },
    { name: 'Jim Green', id: '2' }
  ]);
}
function getStatus(id: string): Promise<string> {
  return new Promise(resolve => {
    if (id === '2') resolve('old');
    else resolve('young');
  });
}
function getListWithStatus(): Promise<Array<{ name: string; id: string; status: string }>> {
  const getList$ = from(defer(() => getList()));
  const getStatus$ = (id: string) => from(defer(() => getStatus(id)));
  const data$ = getList$.pipe(
    mergeMap(list => {
      const queries = list.map(item =>
        getStatus$(item.id).pipe(map(status => ({ ...item, status })))
      );
      return forkJoin(queries);
    })
  );
  return lastValueFrom(data$);
}

The resulting data looks like:

[
  { "name": "John Brown", "id": "1", "status": "young" },
  { "name": "Jim Green", "id": "2", "status": "old" }
]

Conclusion

Observable’s concepts are widely used in reactive programming, and they also bring significant benefits when building front‑end anti‑corruption layers. The article presented several real‑world scenarios where a modest amount of Observable code can replace more verbose Promise‑based implementations. However, for simple cases the added abstraction may be unnecessary; developers should introduce Observable only when the problem complexity justifies it.

Original Source

Signed-in readers can open the original source through BestHub's protected redirect.

Sign in to view source
Republication Notice

This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactadmin@besthub.devand we will review it promptly.

frontendretryrxjsAnti‑Corruption Layerrace conditionobservableData composition
Alibaba Terminal Technology
Written by

Alibaba Terminal Technology

Official public account of Alibaba Terminal

0 followers
Reader feedback

How this landed with the community

Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.