rxjs / redux-observer:observable.retry重新建立连接

时间:2017-07-28 08:20:42

标签: javascript rxjs5 jest redux-observable

我在我正在构建的应用程序中使用elixir phoenix websocket,我有一个看起来像这样的史诗:

const socketObservable = Observable.create((observer: Object) => {
  const socket = new Socket(`${getWebSocketUrl()}/socket`, { params: {
    token: readSession(),
  } });

  socket.connect();

  socket.onOpen(() =>
      observer.next({ type: SOCKET_CONNECTED, socket }),
  );

  socket.onError((error) =>
      observer.error({ type: WEBSOCKET_ERROR, error }),
  );

  return () => {
    // socket.disconnect();
  };
});

const connectToSocket = (
  action$: Object,
) => action$.ofType(CONNECT_TO_SOCKET)
.switchMap(() =>
  socketObservable
    .catch((error) => Observable.of(error)),
)
.retry();

export default connectToSocket;

我希望发生的是,当网络连接消失时,会通过发出{ type: WEBSOCKET_ERROR, error }来通知用户,并在通过发出{ type: SOCKET_CONNECTED, socket }重新建立连接时删除通知。好吧,我得到了第一部分工作,但是当重新连接发生时,{ type: SOCKET_CONNECTED, socket }永远不会发送。使用redux-saga,我能够使用以下代码完成这项工作:

const connectToSocket = (): Object =>
  eventChannel((emitter: (Object) => mixed) => {
    const socket = new Socket(`${getWebSocketUrl()}/socket`, { params: {
      token: readSession(),
    } });

    socket.connect();

    socket.onOpen(() => emitter({ socket }));

    socket.onError((error) => {
      emitter({ error });
    });

    return () => {
      // socket.disconnect();
    };
  });

export function* callConnectToSocket(): Generator<IOEffect, *, *> {
  const chan = yield call(connectToSocket);
  while (true) {
    const { socket, error } = yield take(chan);
    if (socket) {
      yield put({ type: SOCKET_CONNECTED, socket });
    } else {
      yield put({ error, type: WEBSOCKET_ERROR });
    }
  }
}

export function* watchConnectToSocket(): Generator<IOEffect, *, *> {
  yield takeLatest(CONNECT_TO_SOCKET, callConnectToSocket);
}

对于rxjs代码,我认为如果按照documentation for rxjs Observable.retry发出错误,在链的末尾添加.retry()应该触发我的源observable的重试,但可能是我不知道我真的明白retry应该做什么或如何正确使用它。可能有人可以帮助实现我想要的目标。

2 个答案:

答案 0 :(得分:0)

好吧,我终于放弃了在删除连接时抛出错误并将此行observer.error({ type: WEBSOCKET_ERROR, error })更改为observer.next({ type: WEBSOCKET_ERROR, error })。但我仍然想知道retry我做错了什么。任何有关原始代码的帮助将不胜感激。

答案 1 :(得分:0)

要使retry运算符生效,其源可观察性必须产生错误。在您的示例中,似乎错误通知永远不会到达retry,因为catch运算符会从错误中恢复错误。

要使其正常工作,您可以尝试让catch运算符返回首先发出操作然后产生错误的observable:

const connectToSocket = action$ =>
    actions$.ofType(CONNECT_TO_SOCKET)
        .switchMap(() => socketObservable
            .catch(error => Observable.of(error).concat(Observable.throw(error)))
        )
        .retry();

<强>更新

我认为值得一提的是Rx遵循语法next* (complete|error)?,这意味着在同一个观察者的next()之后发出的error()调用将无效。因此,如果socket从错误中恢复并在执行onOpen后执行onError回调,则SOCKET_CONNECTED通知将无法到达消费者。

这可以通过将error替换为next通知或每次发生错误时重新启动socketObservable来处理,这意味着将创建新的socket实例(但这可能不是你想要的)。

这是一个可运行的代码示例,演示了retry的工作原理:

&#13;
&#13;
const { createStore, applyMiddleware } = Redux;
const { createEpicMiddleware } = ReduxObservable;

const socketObservable = Rx.Observable.create(observer => {
    const t1 = setTimeout(() => observer.next({ type: "SOCKET_CONNECTED" }), 200);
    const t2 = setTimeout(() => observer.error({ type: "SOCKET_ERROR" }), 400);

    return () => {
        clearTimeout(t1);
        clearTimeout(t2);
    };
})

const connectToSocket = action$ => action$
    .do(action => console.log(action))
    .ofType("CONNECT_TO_SOCKET")
    .switchMap(() => socketObservable
        .catch(error => Rx.Observable.of(error).concat(Rx.Observable.throw(error)))
        // make 2 attempts to re-connect, i.e. restart socketObservable
        .retry(2)
    )
    // recover in case if both attempts to reconnect have failed
    .retry();

const store = createStore(
    (state, action) => state,
    applyMiddleware(createEpicMiddleware(connectToSocket)));

// dispatch CONNECT_TO_SOCKET two times
Rx.Observable.interval(2000)
    .take(2)
    .subscribe(x => store.dispatch({ type: "CONNECT_TO_SOCKET" }));
&#13;
<script src="https://unpkg.com/rxjs@5.4.2/bundles/Rx.min.js"></script>
<script src="https://unpkg.com/redux@3.7.2/dist/redux.min.js"></script>
<script src="https://unpkg.com/redux-observable@0.14.1/dist/redux-observable.min.js"></script>
&#13;
&#13;
&#13;