DEV Community

loading...

Resolve promises in sequence with RXJS (ConcatMap)

j3nnning profile image Jenning Ho ・1 min read

This article will be a short one mainly on resolving / handling promises in sequence using RXJS. One such example would be fetching a series of data, one after another, but only after the current one resolves.

The go to RXJS operator to achieve this is ConcatMap. When our use case requires sequentiality, ie. queueing a set of events to be resolved in order, we can consider ConcatMap as a possible solution.

ConcatMap when paired with RXJS's Subject will present you an easily modifiable, readable, function that can be adapted to most use cases. Here's the code:

from([1, 2, 3]).pipe(
    concatMap(id => {
        const res$ = new Subject();

        fetch(`url-to-api/${id}`)
            .then(res => res.json())
            .then(res => {
                res$.next(res);
                res$.complete();
            });

        return res$;
    })
).subscribe();
Enter fullscreen mode Exit fullscreen mode

In the ConcatMap operator, we return a Subject.

This way, we can control the flow of the stream, we can decide when is the time to complete the current event and go to the next. The next request will not fire until the current one completes, which we'll do so via .complete() call.

In between the fetch response and the completion of the subject, it is the gap for us to perform any modifications, or actions that is required.

In closing, here's a codepen to demo the code above.

Discussion (1)

pic
Editor guide
Collapse
johncarroll profile image
John Carroll • Edited

Your example can be simplified to:

from([1, 2, 3]).pipe(
  concatMap(id => 
    fetch(`url-to-api/${id}`)
      .then(res => res.json())
  ),
).subscribe()
Enter fullscreen mode Exit fullscreen mode

Operators expecting an ObservableInput<T>, such as concatMap, automatically coerce promises to observables. And observables built from promises automatically complete when the promise resolves.

From the rxjs source code:

export type ObservableInput<T> = SubscribableOrPromise<T> | ArrayLike<T> | Iterable<T>;
Enter fullscreen mode Exit fullscreen mode

If you wanted to modify the promise's response, you can do that with an async function. For example:

from([1, 2, 3]).pipe(
  concatMap(async id => {
    const res = await fetch(`url-to-api/${id}`);
    const json = await res.json();
    // do stuff
    return json;
  }),
).subscribe()
Enter fullscreen mode Exit fullscreen mode