kctang
kctang

Reputation: 11192

Returning source observable value after inner observable emits value

Within an observable chain, I need to perform some async work, then return the source value to the next observable so I had to pipe(mapTo(x)) after the async work.

A more complete example:

// fake async work with just 1 null value
someAsyncWork = () => of(null)

of('1', '2', '3').pipe(
  // some async work
  concatMap(no => someAsyncWork().pipe(mapTo(no))),
  concatMap(no => `Some async work [${no}] done!`)
).subscribe(message => console.log(message))

I cannot use tap(no => someAsyncWork()) because that would cause the next observable to run before someAsyncWork() returns.

While my current approach works, it somewhat clutters the code...and I have this pattern repeated in many places within the codebase.

Question: Anyway to do this without pipe(mapTo(no)) - in a more concise/readable way?

Upvotes: 2

Views: 298

Answers (1)

cartant
cartant

Reputation: 58400

Perhaps the simplest thing to do would be to write your own pipeable operator.

For example:

const concatTap = <T>(project: (value: T) => Observable<any>) =>
  concatMap((value: T) => project(value).pipe(mapTo(value)));

However, that assumes the observable for the async operation emits only a single value. To guard against multiple values being emitted you could do something like this:

const concatTap = <T>(project: (value: T) => Observable<any>) =>
  concatMap((value: T) => concat(project(value).pipe(ignoreElements()), of(value)));

You could use concatTap like this:

of('1', '2', '3').pipe(
  concatTap(() => someAsyncWork()),
  concatMap(no => `Some async work [${no}] done!`)
).subscribe(message => console.log(message));

I'm sure you could choose a better name than I did. concatTap was the first thing that popped into my head. Naming things is hard.

Upvotes: 1

Related Questions