Могу ли я использовать генераторы es6 redux-saga в качестве прослушивателя onmessage для websockets или 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 вызывается как данные, полученные с сервера, но мне не повезло в диспетчеризации действия при вызове yield put(transactions(txs))
в генераторе foo
.
может кто-нибудь сказать мне, что я делаю не так?
1 ответов
сага может быть вызвана только изнутри другой саги (используя yield foo()
или yield call(foo)
) .
в вашем примере foo
Saga вызывается изнутри нормальной функции (onMessage
callback), поэтому он просто вернет объект итератора. Предоставляя итератор (или вызов генератора) из Saga, мы позволяем промежуточному по redux-saga Конст и выполнить итератор для разрешения всех полученных эффектов. Но в коде stream.onmessage = onMessage
просто сделайте простое назначение, чтобы промежуточное ПО ничего не заметило.
что касается главного вопроса. Sagas обычно принимает события из магазина Redux. Вы можете использовать runSaga
для подключения саги к пользовательскому источнику ввода / вывода, но это не будет тривиальным, чтобы применить это к приведенному выше случаю использования. Поэтому я предложу другую альтернативу, используя просто call
эффект. Однако, чтобы ввести его, нам придется перейти от пуш перспектива событий, к тянуть точки зрения.
традиционный способ обработки событий-зарегистрировать прослушиватель событий в некотором источнике событий. Например, назначение onMessage
обратный вызов stream.onmessage
в примере выше. Каждое событие имеет значение толкнул для обратного вызова слушателя. Источник событий находится под полным контролем.
redux-saga принимает другую модель: Sagas тянуть желаемое событие. Как обратные вызовы, они обычно выполняют некоторую обработку. Но они иметь полный контроль над тем, что делать дальше: они могут выбрать, чтобы вытащить то же самое событие снова-что имитирует модель обратного вызова - но они не вынуждены. Они могут выбрать, чтобы вытащить другое событие, начать другую сагу, чтобы взять эстафету или даже прекратить их выполнение. они контролируют собственную логику прогрессии. Все, что может сделать источник событий, это разрешить запросы для будущих событий.
чтобы интегрировать внешние push-источники, нам нужно транспонировать источник событий из push модель в модель тяги; т. е. нам придется построить событие итератор из которого мы можем вытащить будущие события из источника событий
вот пример получения onmessage
итератор от EventSource
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
}
}
}
вышеуказанная функция возвращает объект с nextMessage
метод, который мы можем использовать, чтобы вытащить будущего сообщения. Вызов его вернет обещание, которое будет разрешено со следующим входящим сообщением.
имея 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 вышеуказанного кодекса.
преимуществом вышеуказанного подхода является то, что он сохраняет код внутри саг полностью декларативным (используя только декларативные формы fork
и call
)