Wait for an async operation in onNext of RxJS Observable

前端 未结 3 916
暖寄归人
暖寄归人 2021-02-02 08:15

I have an RxJS sequence being consumed in the normal manner...

However, in the observable \'onNext\' handler, some of the operations will complete synchronously, but oth

3条回答
  •  走了就别回头了
    2021-02-02 09:08

    First of all, move your async operations out of subscribe, it's not made for async operations.

    What you can use is mergeMap (alias flatMap) or concatMap. (I am mentioning both of them, but concatMap is actually mergeMap with the concurrent parameter set to 1.) Settting a different concurrent parameter is useful, as sometimes you would want to limit the number of concurrent queries, but still run a couple concurrent.

    source.concatMap(item => {
      if (item == 'do-something-async-and-wait-for-completion') {
        return Rx.Observable.timer(5000)
          .mapTo(item)
          .do(e => console.log('okay, we can continue'));
        } else {
          // do something synchronously and keep on going immediately
          return Rx.Observable.of(item)
            .do(e => console.log('ready to go!!!'));
        }
    }).subscribe();
    

    I will also show how you can rate limit your calls. Word of advice: Only rate limit at the point where you actually need it, like when calling an external API that allows only a certain number of requests per second or minutes. Otherwise it is better to just limit the number of concurrent operations and let the system move at maximal velocity.

    We start with the following snippet:

    const concurrent;
    const delay;
    source.mergeMap(item =>
      selector(item, delay)
    , concurrent)
    

    Next, we need to pick values for concurrent, delay and implement selector. concurrent and delay are closely related. For example, if we want to run 10 items per second, we can use concurrent = 10 and delay = 1000 (millisecond), but also concurrent = 5 and delay = 500 or concurrent = 4 and delay = 400. The number of items per second will always be concurrent / (delay / 1000).

    Now lets implement selector. We have a couple of options. We can set an minimal execution time for selector, we can add a constant delay to it, we can emit the results as soon as they are available, we can can emit the result only after the minimal delay has passed etc. It is even possible to add an timeout by using the timeout operators. Convenience.

    Set minimal time, send result early:

    function selector(item, delay) {
       return Rx.Observable.of(item)
         .delay(1000) // replace this with your actual call.
         .merge(Rx.Observable.timer(delay).ignoreElements())
    }
    

    Set minimal time, send result late:

    function selector(item, delay) {
       return Rx.Observable.of(item)
         .delay(1000) // replace this with your actual call.
         .zip(Rx.Observable.timer(delay), (item, _))
    }
    

    Add time, send result early:

    function selector(item, delay) {
       return Rx.Observable.of(item)
         .delay(1000) // replace this with your actual call.
         .concat(Rx.Observable.timer(delay).ignoreElements())
    }
    

    Add time, send result late:

    function selector(item, delay) {
       return Rx.Observable.of(item)
         .delay(1000) // replace this with your actual call.
         .delay(delay)
    }
    

提交回复
热议问题