race( timer(2000).pipe(mapTo(1)), timer(1000).pipe(mapTo(2)), ).toPromise().then(r => console.log(r))
Code above will output 2
.
If I have an action that I try to repeat until condition is met but ignore it if it takes too long, then I take an approach in this answer https://stackoverflow.com/a/51644077
The problem is that race
never finishes with the shortest function. The longActionObservable
repeats until the condition is met and empty()
is called.
const o = longActionObservable(); race( o.pipe(expand(v => v < 100 ? empty() : o)), // no toArray because I do not need result timer(2000), ).toPromise()
This code does not return a promise that will ever resolve. I’m wondering why is this approach so fragile to this kind of behavior? If there’s one observable timer(2000)
that most surely ends, why doesn’t race end?
Advertisement
Answer
You are missing an important point about race
:
The observable to emit first is used
This means that if first emission of longActionObservable
occurs before the timer, then timer is not used, regardless of how long the “expand” observable takes to complete.
no toArray because I do not need result
Even though you don’t need the result, toArray
actually makes this work as you wish because it will not allow any emissions until your “expand” observable completes. Instead of using toArray
, you could use reduce
instead:
race( o.pipe( expand(v => v < 100 ? empty() : o), reduce(() => undefined) ), timer(2000), ) .toPromise()
If there’s one observable timer(2000) that most surely ends, why doesn’t race end?
The only reason race will not end is because the chosen source (first observable to emit) does not complete. Check the emissions of your expand observable to see why it isn’t completing:
o.pipe( expand(v => v < 100 ? empty() : o), tap(v => console.log('expand: ', v)), reduce(() => undefined) ),
Here’s a StackBlitz you can play around with 🙂