我可以使用 redux-saga 的 es6 生成器作为 websockets 或 eventsource 的 onmessage 监听器吗?

2024-04-26

我正在尝试让 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.

谁能告诉我我做错了什么?


一个 Saga 只能从另一个 Saga 内部调用(使用yield foo() or yield call(foo)) .

在你的例子中,fooSaga 是从普通函数内部调用的(onMessage回调),因此它只会返回迭代器对象。通过从 Saga 生成迭代器(或对生成器的调用),我们允许 redux-saga 中间件截距那个电话和run迭代器以解析所有产生的效果。但在你的代码中,stream.onmessage = onMessage只需做一个简单的分配,这样中间件就不会注意到任何事情。

至于主要问题。 Sagas 通常从 Redux 存储中获取事件。您可以使用runSaga将传奇连接到自定义输入/输出源,但将其应用于上述用例并不简单。所以我会提出另一种选择,简单地使用call影响。然而,为了引入它,我们必须从push事件的视角,pull看法。

处理事件的传统方法是在某个事件源上注册一些事件侦听器。就像分配onMessage回调到stream.onmessage在上面的例子中。每个事件的发生是pushed到侦听器回调。事件源完全受控。

redux-saga 采用不同的模型:Sagaspull所需的事件。作为回调,它们通常会进行一些处理。但他们对下一步要做什么有完全的控制权:他们可能会选择再次拉取相同的事件——这模仿了回调模型——但他们并不是被迫这样做。他们可能选择拉动另一个事件,启动另一个 Saga 来接力,甚至终止他们的执行。也就是说,他们控制着自己的进展逻辑。事件源所能做的就是解决对未来事件的查询。

为了集成外部推送源,我们需要将事件源从推送模型转置为拉取模型;即我们必须建立一个事件迭代器我们可以从事件源中提取未来事件

这是一个推导的例子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我们可以用来提取未来消息的方法。调用它将返回一个 Promise,该 Promise 将通过下一条传入消息进行解析。

拥有createSourceAPI函数。我们现在可以通过简单的方式使用它call effect

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)
}

你可以找到一个现场运行演示 https://jsbin.com/wumuri/edit?js,console上面的代码。

上述方法的一个优点是它使 Sagas 中的代码保持完全声明性(仅使用声明性形式)fork and call)

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

我可以使用 redux-saga 的 es6 生成器作为 websockets 或 eventsource 的 onmessage 监听器吗? 的相关文章

随机推荐