2

たとえば、この jsbin の例のコードは次のとおりです。

const pingEpic = action$ =>
  action$.ofType(PING)
    .delay(1000) // Asynchronously wait 1000ms then continue
    .mapTo({ type: PONG })
    .takeUntil(action$.ofType(CANCEL));

上記のように使用するとtakeUntil、アクションと 1 秒の遅延をディスパッチした後、CANCELアクションが再び起動することはありません。なんで?

4

1 に答える 1

12

問題は、RxJS がどのように機能するかについての微妙ではあるが重大な誤解です。しかし、これは非常に一般的です。

あなたの例を考えると:

const pingEpic = action$ =>
  action$.ofType(PING)
    .delay(1000)
    .mapTo({ type: PONG })
    .takeUntil(action$.ofType(CANCEL));

このエピックの動作は、 type に一致しないすべてのアクションを除外するものとして説明できますPING。アクションが一致したら、1000 ミリ秒待ってから、そのアクションを別の action にマップします{ type: PONG }。これは発行され、redux-observable によってディスパッチされます。アプリの実行中にいつでもCANCEL誰かが typeのアクションをディスパッチすると、ソースからサブスクライブ解除されます。つまり、このチェーン全体がサブスクライブ解除され、エピックが終了します。

命令的に行った場合、これがどのように見えるかを確認すると役立つ場合があります。

const pingEpic = action$ => {
  return new Rx.Observable(observer => {
    console.log('[pingEpic] subscribe');
    let timer;

    const subscription = action$.subscribe(action => {
      console.log('[pingEpic] received action: ' + action.type);

      // When anyone dispatches CANCEL, we stop listening entirely!
      if (action.type === CANCEL) {
        observer.complete();
        return;
      }

      if (action.type === PING) {
        timer = setTimeout(() => {
          const output = { type: PONG };
          observer.next(output);
        }, 1000);
      }
    });

    return {
      unsubscribe() {
        console.log('[pingEpic] unsubscribe');
        clearTimeout(timer);
        subscription.unsubscribe();
      }
    };
  });
};

このコードは、次の偽のストアで実行できます: http://jsbin.com/zeqasih/edit?js,console


代わりに、通常は、キャンセル可能にしたいサブスクライバー チェーンを、無期限にリッスンするトップレベル チェーンから隔離する必要があります。あなたの例(ドキュメントから修正されたもの)は不自然ですが、最初にそれを実行しましょう。

ここでは、mergeMapオペレーターを使用して、一致したアクションを実行し、別の別のオブザーバブル チェーンにマップできるようにします。

デモ: http://jsbin.com/nofato/edit?js,output

const pingEpic = action$ =>
  action$.ofType(PING)
    .mergeMap(() =>
      Observable.timer(1000)
        .takeUntil(action$.ofType(CANCEL))
        .mapTo({ type: PONG })
    );

以前は 1000 ミリ秒待機してObservable.timerから、それが発行する値 (たまたま数字の 0 ですが、ここでは重要ではありません) をPONGアクションにマップしていました。また、タイマー ソースが正常に完了するか、 type のアクションを受け取るまで、タイマー ソースから「取得」したいとも言いますCANCEL

これにより、チェーンが分離mergeMapされます。これは、エラーが発生するか完了するまで、返されたオブザーバブルをサブスクライブし続けるためです。しかし、それが発生した場合、適用先のソースへのサブスクライブを停止することはありません。action$.ofType(PING)この例では。

より現実的な例は、取り消しセクションの redux-observable ドキュメントにあります。

ここでは、.takeUntil() を .mergeMap() 内の後、ただし AJAX 呼び出しの後に配置しました。Epic が将来のアクションをリッスンするのを停止するのではなく、AJAX リクエストのみをキャンセルしたいので、これは重要です。

const fetchUserEpic = action$ =>
  action$.ofType(FETCH_USER)
    .mergeMap(action =>
      ajax.getJSON(`/api/users/${action.payload}`)
        .map(fetchUserFulfilled)
        .takeUntil(action$.ofType(FETCH_USER_CANCELLED))
    );

これはすべて混乱するように聞こえるかもしれませんが、最も強力なものと同様に、一度理解すれば直感的に理解できます。Ben Lesh は、最近の講演でObservables がどのように機能するかを説明する優れた仕事をしています. AngularConnect での話ですが、Angular 固有のものではありません。


余談ですが、たとえば受信アクションを別の異なるアクションにマップする場合など、エピックがアクションを飲み込んだり、レデューサーに到達するのを妨げたりしないことに注意することが重要です。実際、エピックがアクションを受け取ったとき、それはすでに reducers を通過しています。エピックは、アプリ アクションのストリームをリッスンするサイドカー プロセスと考えてください。ただし、通常の redux の発生を防ぐことはできず、新しいアクションを発行することしかできません。

于 2016-11-18T01:24:17.660 に答える