race( timer(2000).pipe(mapTo(1)), timer(1000).pipe(mapTo(2)), ).toPromise().then(r => console.log(r))
Code above will output
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?
You are missing an important point about
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
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 🙂