web-dev-qa-db-ja.com

Rxjsがエラーで購読を解除するのはなぜですか?

要するに:すべての_.catch_の前に_.subscribe_を置くことなく、ストリームのエラーの後にリスニングを続行するにはどうすればよいですか?

詳細が必要な場合は、こちらをご覧ください。

現在のユーザーのサブジェクトまたはnullがあると仮定します。 APIからデータを取得して、サブジェクトに送信することがあります。それに応じてビューを更新します。しかし、ある時点でサーバーでエラーが発生し、アプリケーションを以前と同じように動作させ続けたいのですが、エラーについていくつかの場所に通知し、件名をリッスンし続けます。

当初、userSubject.error(...)を実行すると、サブスクライブ時に_.catch_コールバックとerrorハンドラーのみがトリガーされ、すべての成功ハンドラーとチェーンがスキップされると思いました。そして、userSubject.next(...)を呼び出した後、すべてのチェーンとサブスクライバーは以前と同じように機能します

しかし、残念ながらそうではありません。最初に捕捉されなかった_.error_の後、サブスクライバーはストリームからサブスクライブ解除され、サブスクライバーは動作しなくなります。

だから私の質問:なぜですか?また、null値を通常どおり処理したいが、一部の場所でのみエラーを処理したい場合は、代わりにどうすればよいですか?

サブスクライバーがエラーでサブスクライブを解除するRxJsソースコードへのリンクは次のとおりです https://github.com/ReactiveX/rxjs/blob/master/src/Subscriber.ts#L14

10
mgrinko

Rx observables 文法に従うnext*(error|complete)?、つまり、errorまたはcomplete通知が配信された後は何も生成できません。

これが重要である理由の説明は Rx設計ガイドライン から見つけることができます:

監視可能なシーケンスが終了したことを示す単一のメッセージにより、監視可能なシーケンスのコンシューマーは、クリーンアップ操作を安全に実行できることを決定論的に確立できます。

さらに、単一障害により、複数の監視可能なシーケンスで動作する演算子のアボートセマンティクスを維持できます。

つまり、サーバーエラーが発生した後もオブザーバーがサブジェクトをリッスンし続けるようにしたい場合は、そのエラーをサブジェクトに配信せず、他の方法で処理します(たとえば、catchretryまたは専用のサブジェクトにエラーを配信します)。

11
Sergey Karavaev

すべてのObservableは、0個以上のnext通知と1個のerrorまたはcompleteを発行しますが、両方を発行することはありません。

このため、サブジェクトには内部状態があります。

次に、チェーンをどのように構築するかによって異なります。たとえば、retry()を使用して、エラー時にそのソースObservableに再サブスクライブできます。

または、サブジェクトに値を渡す場合、next通知のみを送信し、他の2つは無視できます。

.subscribe(v => subject.next(v));

または、ユーザーがnullのときにエラーをスローする場合は、例外をキャプチャしてエラー通知として送信する任意の演算子を使用できます。たとえば、次のようになります。

.map(v => {
    if (v === null) {
        throw new Error("It's broken");
    }
    return v;
})

とにかく、コードなしでより正確なアドバイスを与えるのは難しいです。

5
martin