web-dev-qa-db-ja.com

RxJS ObservableのonNextで非同期操作を待つ

通常の方法で消費されるRxJSシーケンスがあります...

ただし、observable 'onNext'ハンドラーでは、一部の操作は同期的に完了しますが、他の操作は非同期コールバックを必要とし、入力シーケンスの次のアイテムを処理する前に待機する必要があります。

...これを行う方法が少し混乱しました。何か案は?ありがとう!

someObservable.subscribe(
    function onNext(item)
    {
        if (item == 'do-something-async-and-wait-for-completion')
        {
            setTimeout(
                function()
                {
                    console.log('okay, we can continue');
                }
                , 5000
            );
        }
        else
        {
            // do something synchronously and keep on going immediately
            console.log('ready to go!!!');
        }
    },
    function onError(error)
    {
        console.log('error');
    },
    function onComplete()
    {
        console.log('complete');
    }
);
28
user3291110

実行する各操作は、オブザーバブルとしてモデル化できます。同期操作でもこの方法でモデル化できます。次に、mapを使用してシーケンスをシーケンスのシーケンスに変換し、concatAllを使用してシーケンスをフラット化します。

someObservable
    .map(function (item) {
        if (item === "do-something-async") {
            // create an Observable that will do the async action when it is subscribed
            // return Rx.Observable.timer(5000);

            // or maybe an ajax call?  Use `defer` so that the call does not
            // start until concatAll() actually subscribes.
            return Rx.Observable.defer(function () { return Rx.Observable.ajaxAsObservable(...); });
        }
        else {
            // do something synchronous but model it as an async operation (using Observable.return)
            // Use defer so that the sync operation is not carried out until
            // concatAll() reaches this item.
            return Rx.Observable.defer(function () {
                return Rx.Observable.return(someSyncAction(item));
            });
        }
    })
    .concatAll() // consume each inner observable in sequence
    .subscribe(function (result) {
    }, function (error) {
        console.log("error", error);
    }, function () {
        console.log("complete");
    });

コメントの一部に返信するには、ある時点で、関数のストリームに何らかの期待をかける必要があります。ほとんどの言語では、非同期の可能性がある関数を扱う場合、関数のシグネチャは非同期であり、関数の実際の非同期と同期の性質は、関数の実装の詳細として隠されます。これは、javascript promise、Rx observables、c#Tasks、c ++ Futuresなどを使用している場合に当てはまります。関数は最終的にpromise/observable/task/future/etcを返し、関数が実際に同期している場合、それが返すオブジェクトはすでに完了しました。

とは言っても、これはJavaScriptなので、canカンニング:

var makeObservable = function (func) {
    return Rx.Observable.defer(function () {
        // execute the function and then examine the returned value.
        // if the returned value is *not* an Rx.Observable, then
        // wrap it using Observable.return
        var result = func();
        return result instanceof Rx.Observable ? result: Rx.Observable.return(result);
    });
}

someObservable
    .map(makeObservable)
    .concatAll()
    .subscribe(function (result) {
    }, function (error) {
        console.log("error", error);
    }, function () {
        console.log("complete");
    });
25
Brandon

まず、非同期操作をsubscribeから移動します。これは非同期操作のために作成されたものではありません。

使用できるのは mergeMap (別名flatMap)または concatMap です。 (私はそれらの両方に言及していますが、concatMapは実際にはmergeMapパラメーターが1に設定された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();
_

また、通話をレート制限する方法も示します。 アドバイス: 1秒または1分あたり特定の数のリクエストのみを許可する外部API。それ以外の場合は、同時操作の数を制限し、システムを最大速度で移動させることをお勧めします。

次のスニペットから始めます。

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

次に、concurrentdelayの値を選択し、selectorを実装する必要があります。 concurrentdelayは密接に関連しています。たとえば、1秒間に10個のアイテムを実行する場合、_concurrent = 10_および_delay = 1000_(ミリ秒)を使用できますが、_concurrent = 5_および_delay = 500_または_concurrent = 4_も使用できますおよび_delay = 400_。 1秒あたりのアイテム数は常にconcurrent / (delay / 1000)になります。

selectorを実装しましょう。いくつかのオプションがあります。 selectorの最小実行時間を設定し、一定の遅延を追加し、利用可能になるとすぐに結果を出力できます。最小遅延が経過した後にのみ結果を出力できます。 。 timeout 演算子を使用してタイムアウトを追加することもできます。便利さ。

最小限の時間を設定し、結果を早めに送信します。

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

最短時間を設定し、結果を遅く送信します。

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

時間を追加して、結果を早めに送信します。

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

時間を追加し、結果を遅く送信します。

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

手動の非同期操作を行う別の簡単な例。

それは良いリアクティブな実践ではないことに注意してください! 1000ミリ秒だけ待機する場合は、Rx.Observable.timerまたは遅延演算子を使用します。

someObservable.flatMap(response => {
  return Rx.Observable.create(observer => {
    setTimeout(() => {
      observer.next('the returned value')
      observer.complete()
    }, 1000)
  })
}).subscribe()

次に、setTimeoutをImage.onloadやfileReader.onloadなどの非同期関数に置き換えます...

0
TeChn4K