2

デバイスを複数回クエリする必要があります。すべてのクエリは非同期である必要があり、デバイスは一度に同時クエリをサポートしていません。また、一度問い合わせると、すぐに再度問い合わせることができません。正常に動作するには、少なくとも 1 秒の一時停止が必要です。

saveClock()とによって実行される私の 2 つのクエリsaveConfig()は Promise を返し、両方とも undefined を期待どおりに返すことで解決します。

次のコードで、削除すると呼び出されtake()ないのはなぜですか? ここで何が起こっているのか、同じ動作を実現するためのより良い方法はありますか?toArray()

export const saveEpic = (action$, store) =>
  action$.ofType(SAVE)
    .map(action => {
      // access store and create object data
      // ...
      return data;
    })
    .mergeMap(data =>
      Rx.Observable.from([
        Rx.Observable.of(data).mergeMap(data => saveClock(data.id, data.clock)),
        Rx.Observable.timer(1000),
        Rx.Observable.of(data).mergeMap(data => saveConfig(data.id, data.config)),
        Rx.Observable.of(data.id)
     ])
    )
    .concatAll()
    .take(4)
    .toArray()
    // [undefined, 0, undefined, "id"]
    .map(x => { type: COMPLETED, id: x[3] });
4

1 に答える 1

2

私が見るいくつかのことがあります:

最後.map()に括弧がありません。現在の形式では構文エラーですが、微妙な変更により、オブジェクトを返す代わりに誤ってラベル付きステートメントになる可能性があります。現在の形式では構文エラーであるため、これはこの投稿の単なるバグであり、コードのバグではなく (実行すらされない)、再確認してください!

// before
.map(x => { type: COMPLETED, id: x[3] });

// after
.map(x => ({ type: COMPLETED, id: x[3] }));

それが修正されたので、この例は単純な redux-observable テスト ケースで実行されます: http://jsbin.com/hunale/edit?js,outputしたがって、私があなたとは異なる方法で行った注目すべき点が何もない場合、問題コードではなくコードにあるようです。提供された。追加の洞察を自由に追加するか、さらに良いことに、JSBin/git リポジトリで再現してください。


あなたが言及しなかったが非常に注目すべきことの1つは、redux-observableでは、エピックは通常、長寿命の「プロセスマネージャー」になるということです。この叙事詩は、実際にはこれらの保存の 1 つだけを処理し、次に complete() を処理しますが、これはおそらく実際に必要なものではありませんか? ユーザーは、アプリケーションの起動ごとに 1 回だけ何かを保存できますか? ありそうもない。

代わりに、エピックが返すトップレベル ストリームを維持し、このロジックをmergeMap. take(4)と を渡すと、無関係data.idになります。

const saveEpic = (action$, store) =>
  action$.ofType(SAVE)
    .mergeMap(data =>
      Rx.Observable.from([
        Rx.Observable.of(data).mergeMap(data => saveClock(data.id, data.clock)),
        Rx.Observable.timer(1000),
        Rx.Observable.of(data).mergeMap(data => saveConfig(data.id, data.config))
      ])
      .concatAll()
      .toArray()
      .map(() => ({ type: COMPLETED, id: data.id }))
    );

このストリームの分離については、Ben Lesh が最近の AngularConnect トークでエラーのコンテキストで説明していますが、それでも適用できます: https://youtu.be/3LKMwkuK0ZE?t=20m (心配しないでください。これは Angular 固有のものではありません) !)

次に、皆さんの生活を楽にするかもしれない一方的なリファクタリングのアドバイスをいくつか共有したいと思います。

イベントの順序を視覚的により正確に反映するようにリファクタリングし、複雑さを軽減します。

const saveEpic = (action$, store) =>
  action$.ofType(SAVE)
    .mergeMap(data =>
      Rx.Observable.from(saveClock(data.id, data.clock))
        .delay(1000)
        .mergeMap(() => saveConfig(data.id, data.config))
        .map(() => ({ type: COMPLETED, id: data.id }))
    );

ここでは、 によって返された Promise を消費し、そのsaveClock出力を 1000 ミリ秒遅らせsaveConfig()ます。最後に、その結​​果をCOMPLETEアクションにマッピングします。

最後に、Epic が存続し、存続期間が長い場合、この Epicに現状のままで、他の SAVE リクエストがまだ実行中か、必要な 1000 ミリ秒の遅延をまだ使い果たしていない間に複数の SAVE リクエストを受信するのを止めるものは何もないことに注意してください。要求の間。つまり、リクエスト間に 1000 ミリ秒のスペース実際に必要な場合、エピック自体は、UI コードがそれを壊すのを完全に防ぐわけではありません。その場合、たとえば..zip()BehaviorSubject

http://jsbin.com/waqipol/edit?js,output

const saveEpic = (action$, store) => {
  // used to control how many we want to take,
  // the rest will be buffered by .zip()
  const requestCount$ = new Rx.BehaviorSubject(1)
    .mergeMap(count => new Array(count));

  return action$.ofType(SAVE)
    .zip(requestCount$, action => action)
    .mergeMap(data =>
      Rx.Observable.from(saveClock(data.id, data.clock))
        .delay(1000)
        .mergeMap(() => saveConfig(data.id, data.config))
        .map(() => ({ type: COMPLETED, id: data.id }))
        // we're ready to take the next one, when available
        .do(() => requestCount$.next(1))
    );
};

これにより、既存のリクエストをまだ処理している間に入ってくる保存リクエストがバッファリングされ、一度に 1 つだけ取得されるようになります。ただし、これは無制限のバッファーであることに注意してください。つまり、保留中のアクションのキューは、バッファーがフラッシュされるよりも無限に速くなる可能性があります。重複するリクエストをドロップするなど、損失の多いバックプレッシャーの戦略を採用しない限り、これは避けられません。

リクエストを 1 秒間に 2 回以上送信しないという要件が重複する他のエピックがある場合は、すべてのエピックに対してこの保証を行う何らかの単一のスーパーバイザを作成する必要があります。

これはすべて非常に複雑に思えるかもしれませんが、おそらく皮肉なことに、RxJS では、従来の命令型コードよりもはるかに簡単に実行できます。最も難しい部分は、実際にパターンを知ることです。

于 2016-11-08T18:42:52.257 に答える