我可以使用 redux-saga 的 es6 生成器作为 websockets 或 eventsource 的 onmessage 监听器吗?
Can I use redux-saga's es6 generators as onmessage listener for websockets or eventsource?
我正在尝试让 redux-saga 与 onmessage
侦听器一起工作。我不知道为什么我的东西不起作用。
我有以下设置。
// sagas.js
import { take, put } from 'redux-saga';
import {transactions} from "./actions";
function* foo (txs) {
console.log("yielding"); // appears in console
yield put(transactions(txs)); // action *is not* dispatched
console.log("yielded"); //appears in console
}
const onMessage = (event) => {
const txs = JSON.parse(event.data);
const iter = foo(txs);
iter.next(); // do I really need to do this?
};
function* getTransactions() {
while(yield take('APP_LOADED')) {
const stream = new EventSource(eventSourceUrl);
stream.onopen = onOpen;
stream.onmessage = onMessage;
stream.onerror = onError;
// this is just testing that `yield put` works
yield put(transactions([{baz : 42}])); //this action *is* dispatched
}
};
当调度 APP_LOADED
操作时 getTransactions
被调用,流被打开并且 onMessage 侦听器被调用,因为从服务器接收到数据,但我没有任何运气在生成器 foo
.
中调用 yield put(transactions(txs))
时分派操作
谁能告诉我我做错了什么?
一个 Saga 只能从另一个 Saga 中调用(使用 yield foo()
或 yield call(foo)
)。
在您的示例中,foo
Saga 是从普通函数(onMessage
回调)内部调用的,因此它只是 return 迭代器对象。通过从 Saga 生成迭代器(或调用生成器),我们允许 redux-saga 中间件 拦截 调用和 运行 迭代器以解决所有产生的效果。但是在你的代码中,stream.onmessage = onMessage
只是做一个简单的赋值,所以中间件不会注意到任何东西。
至于主要问题。 Sagas 通常从 Redux 存储中获取事件。您可以使用 runSaga
将 saga 连接到自定义 input/output 源,但将其应用于上述用例并非易事。因此,我将提出另一种仅使用 call
效果的替代方案。但是,为了介绍它,我们必须从事件的 push 视角转换到 pull 视角。
传统的事件处理方式是在某个事件源上注册一些事件监听器。就像在上面的示例中将 onMessage
回调分配给 stream.onmessage
一样。每个事件的发生都推送 到侦听器回调。事件源处于完全控制之中。
redux-saga 采用不同的模型:Sagas 拉 所需的事件。作为回调,它们通常会做一些处理。但是他们可以完全控制下一步要做什么:他们可以选择再次拉同一个事件——模仿回调模型——但他们不是被迫的。他们可能会选择拉另一个事件,启动另一个 Saga 来接力甚至终止他们的执行。也就是说,他们可以控制自己的进展逻辑。事件源所能做的就是解决对未来事件的查询。
要集成外部推送源,我们需要将事件源从推送模型转换为拉取模型;也就是说,我们必须构建一个 事件迭代器 ,我们可以从中提取事件源中的未来事件
这是从 EventSource
派生 onmessage
迭代器的示例
function createSource(url) {
const source = new EventSource(url)
let deferred
source.onmessage = event => {
if(deferred) {
deferred.resolve(JSON.parse(event.data))
deferred = null
}
}
return {
nextMessage() {
if(!deferred) {
deferred = {}
deferred.promise =
new Promise(resolve => deferred.resolve = resolve)
}
return deferred.promise
}
}
}
上面的函数 return 是一个带有 nextMessage
方法的对象,我们可以用它来提取未来的消息。调用它会 return 一个 Promise,它将在下一条传入消息中解析。
具有createSource
API的功能。我们现在可以通过一个简单的call
效果
来使用它
function* watchMessages(msgSource) {
let txs = yield call(msgSource.nextMessage)
while(txs) {
yield put(transactions(txs))
txs = yield call(msgSource.nextMessage)
}
}
function* getTransactionsOnLoad() {
yield take('APP_LOADED')
const msgSource = yield call(createSource, '/myurl')
yield fork(watchMessages, msgSource)
}
上面的代码你可以找一个live running demo
上述方法的一个优点是它使 Sagas 中的代码完全声明(仅使用声明形式 fork
和 call
)
我正在尝试让 redux-saga 与 onmessage
侦听器一起工作。我不知道为什么我的东西不起作用。
我有以下设置。
// sagas.js
import { take, put } from 'redux-saga';
import {transactions} from "./actions";
function* foo (txs) {
console.log("yielding"); // appears in console
yield put(transactions(txs)); // action *is not* dispatched
console.log("yielded"); //appears in console
}
const onMessage = (event) => {
const txs = JSON.parse(event.data);
const iter = foo(txs);
iter.next(); // do I really need to do this?
};
function* getTransactions() {
while(yield take('APP_LOADED')) {
const stream = new EventSource(eventSourceUrl);
stream.onopen = onOpen;
stream.onmessage = onMessage;
stream.onerror = onError;
// this is just testing that `yield put` works
yield put(transactions([{baz : 42}])); //this action *is* dispatched
}
};
当调度 APP_LOADED
操作时 getTransactions
被调用,流被打开并且 onMessage 侦听器被调用,因为从服务器接收到数据,但我没有任何运气在生成器 foo
.
yield put(transactions(txs))
时分派操作
谁能告诉我我做错了什么?
一个 Saga 只能从另一个 Saga 中调用(使用 yield foo()
或 yield call(foo)
)。
在您的示例中,foo
Saga 是从普通函数(onMessage
回调)内部调用的,因此它只是 return 迭代器对象。通过从 Saga 生成迭代器(或调用生成器),我们允许 redux-saga 中间件 拦截 调用和 运行 迭代器以解决所有产生的效果。但是在你的代码中,stream.onmessage = onMessage
只是做一个简单的赋值,所以中间件不会注意到任何东西。
至于主要问题。 Sagas 通常从 Redux 存储中获取事件。您可以使用 runSaga
将 saga 连接到自定义 input/output 源,但将其应用于上述用例并非易事。因此,我将提出另一种仅使用 call
效果的替代方案。但是,为了介绍它,我们必须从事件的 push 视角转换到 pull 视角。
传统的事件处理方式是在某个事件源上注册一些事件监听器。就像在上面的示例中将 onMessage
回调分配给 stream.onmessage
一样。每个事件的发生都推送 到侦听器回调。事件源处于完全控制之中。
redux-saga 采用不同的模型:Sagas 拉 所需的事件。作为回调,它们通常会做一些处理。但是他们可以完全控制下一步要做什么:他们可以选择再次拉同一个事件——模仿回调模型——但他们不是被迫的。他们可能会选择拉另一个事件,启动另一个 Saga 来接力甚至终止他们的执行。也就是说,他们可以控制自己的进展逻辑。事件源所能做的就是解决对未来事件的查询。
要集成外部推送源,我们需要将事件源从推送模型转换为拉取模型;也就是说,我们必须构建一个 事件迭代器 ,我们可以从中提取事件源中的未来事件
这是从 EventSource
onmessage
迭代器的示例
function createSource(url) {
const source = new EventSource(url)
let deferred
source.onmessage = event => {
if(deferred) {
deferred.resolve(JSON.parse(event.data))
deferred = null
}
}
return {
nextMessage() {
if(!deferred) {
deferred = {}
deferred.promise =
new Promise(resolve => deferred.resolve = resolve)
}
return deferred.promise
}
}
}
上面的函数 return 是一个带有 nextMessage
方法的对象,我们可以用它来提取未来的消息。调用它会 return 一个 Promise,它将在下一条传入消息中解析。
具有createSource
API的功能。我们现在可以通过一个简单的call
效果
function* watchMessages(msgSource) {
let txs = yield call(msgSource.nextMessage)
while(txs) {
yield put(transactions(txs))
txs = yield call(msgSource.nextMessage)
}
}
function* getTransactionsOnLoad() {
yield take('APP_LOADED')
const msgSource = yield call(createSource, '/myurl')
yield fork(watchMessages, msgSource)
}
上面的代码你可以找一个live running demo
上述方法的一个优点是它使 Sagas 中的代码完全声明(仅使用声明形式 fork
和 call
)