在 redux-Saga 中重新连接到 webSocket 服务器的标准方法?
Posted
技术标签:
【中文标题】在 redux-Saga 中重新连接到 webSocket 服务器的标准方法?【英文标题】:Standard way of reconnecting to webSocket server in redux-Saga? 【发布时间】:2020-01-21 09:50:16 【问题描述】:我正在尝试使用 redux-saga 从我的 react App 连接到 websocket 服务器并希望捕获连接丢失(服务器错误,重新启动),以便每隔 4 秒重新连接一次,直到连接再次恢复。问题在于重新连接到 webSocket,redux 存储不再更新。
我尝试按照以下代码使用 redux-saga 的 eventChannel。不幸的是,没有或至少我找不到任何文档回答 ws reconnect in redux-saga。
import eventChannel from 'redux-saga';
import all, takeEvery, put, call, take, fork from 'redux-saga/effects'
import INITIALIZE_WS_CHANNEL from "../../constants/ActionTypes"
import updateMarketData from "../actions"
function createEventChannel()
return eventChannel(emit =>
//Subscribe to websocket
const ws = new WebSocket('ws://localhost:9000/rates');
ws.onopen = () =>
console.log("Opening Websocket");
;
ws.onerror = error =>
console.log("ERROR: ", error);
;
ws.onmessage = e =>
return emit(data: JSON.parse(e.data))
;
ws.onclose = e =>
if (e.code === 1005)
console.log("WebSocket: closed");
else
console.log('Socket is closed Unexpectedly. Reconnect will be attempted in 4 second.', e.reason);
setTimeout(() =>
createEventChannel();
, 4000);
;
return () =>
console.log("Closing Websocket");
ws.close();
;
);
function * initializeWebSocketsChannel()
const channel = yield call(createEventChannel);
while (true)
const data = yield take(channel);
yield put(updateMarketData(data));
export function * initWebSocket()
yield takeEvery(INITIALIZE_WS_CHANNEL, initializeWebSocketsChannel);
export default function* rootSaga()
yield all ([
fork(initWebSocket)
]);
更新
为了完成@azundo 为寻找 websocket 和 redux-saga 完整示例的人接受的答案,我添加了以下代码:
function * initializeWebSocketsChannel()
console.log("going to connect to WS")
const channel = yield call(createEventChannel);
while (true)
const data = yield take(channel);
yield put(updateMarketData(data));
export function * startStopChannel()
while (true)
yield take(START_CHANNEL);
yield race(
task: call(initializeWebSocketsChannel),
cancel: take(STOP_CHANNEL),
);
//if cancel wins the race we can close socket
ws.close();
export default function* rootSaga()
yield all ([
startStopChannel()
]);
START_CHANNEL 和 STOP_CHANNEL 动作可以在 componentDidMount中调用> 和 componentWillUnmount 分别是 react 组件的生命周期。
【问题讨论】:
我看到了你的回答,使用 componentDidMount 和 componentWillUnmount 听起来是个好主意,但是如果我们在 componentDidMount 上断开连接,一段时间后我们想再次连接怎么办? 【参考方案1】:这不起作用的原因是因为您对createEventChannel
的递归调用没有被yield
ed 到传奇中间件redux-saga
无法知道后续事件通道的创建。您可能希望在事件通道中定义递归函数,请参见下面的代码,因此只有一个 eventChannel
始终挂接到存储中。
还要注意在预期的套接字关闭时添加了发射 END,这样如果您不重新连接,就不会让 eventChannel 永远打开。
import eventChannel, END from 'redux-saga';
let ws; //define it here so it's available in return function
function createEventChannel()
return eventChannel(emit =>
function createWs()
//Subscribe to websocket
ws = new WebSocket('ws://localhost:9000/rates');
ws.onopen = () =>
console.log("Opening Websocket");
;
ws.onerror = error =>
console.log("ERROR: ", error);
;
ws.onmessage = e =>
return emit(data: JSON.parse(e.data))
;
ws.onclose = e =>
if (e.code === 1005)
console.log("WebSocket: closed");
// you probably want to end the channel in this case
emit(END);
else
console.log('Socket is closed Unexpectedly. Reconnect will be attempted in 4 second.', e.reason);
setTimeout(() =>
createWs();
, 4000);
;
createWs();
return () =>
console.log("Closing Websocket");
ws.close();
;
);
【讨论】:
我面临的一个问题是您将如何使用此代码实现发送逻辑?我们需要一个回调或函数,一旦连接就返回套接字对象。 如果您只需要单例访问,一个有点老套的选择是导出一个返回内部ws
的getWebsocketSingleton
函数。您可以在模块顶部设置ws = null;
,在这种情况下在清理中设置,然后在其他地方运行ws.send
之前检查ws === null
。如果使用这样的事件通道解决方案,不确定是否有更好的方法来公开 websocket。以上是关于在 redux-Saga 中重新连接到 webSocket 服务器的标准方法?的主要内容,如果未能解决你的问题,请参考以下文章
连接到 WebSocket 时如何修复“Access-Control-Allow-Origin”错误?