redux-saga原理

redux,saga,原理 · 浏览次数 : 144

小编点评

**redux-saga 中的原理总结** **1. 标准的中间件模式** - 使用者创建一个 middleware,用于处理应用程序中的副作用。 - middleware 使用 `takeEvery`、`take` 和 `put` 方法与 Redux 中的 `dispatch` 方法类似。 **2. 异步操作的处理** - middleware 使用 `fork` 和 `call` 方法创建异步任务,并将结果发布到 Redux 中。 - `delay` 和 `setTimeout` 等函数用于模拟异步操作。 **3. 副作用和状态管理** - middleware 在 `switch` 语句中处理不同类型的副作用,并更新状态。 - 它使用 `select` 和 `put` 方法来管理副作用和状态。 **4. 串行和并行** - middleware 支持串行和并行,以处理复杂的异步流程。 - 它使用 `yield` 和 `yield*` 迭代器来实现并发操作。 **5. 示例** ```javascript // 创建 saga 中的 middleware const sagaMiddleware = createSagaMiddleware(); // 创建 Redux store const store = createStore(reducer, applyMiddleware(sagaMiddleware, thunk)); // 启动 saga sagaMiddleware.run(main); ``` **6. 关键概念** - **middleware**:一个中间件,处理应用程序中的副作用。 - ** saga**:一个用于处理异步操作的 saga 中的独立组件。 - **effect**:一个用于执行异步操作的 JS 对象,包含相关信息。 - **channel**:用于进行通道的发布订阅。 - **state**:Redux 中用于状态管理的键值对。 - **task queue**:用于处理异步操作的队列。

正文

前言

工作中使用了redux-saga这个redux中间件,如果不明白内部原理使用起来会让人摸不着头脑,阅读源码后特意对其原理做下总结。

redux的特点

  • 一个标准、管理应用副作用的redux中间件

  • 实现切面编程方式

  • 声明式的编写方式

  • 订阅发布的设计模式

    优点:

  1. 把异步操作转移到单独 saga文件中,而不是糅杂在action或者component中;

  2. dispatch的参数保持为纯粹的action而不是thunk function;

  3. 大量的saga辅助函数和effect创建器减少了开发者的开发成本;

  4. 灵活的串行或并行能够实现复杂的异步流程。

分析原理

先举个实践的例子

import { createStore, applyMiddleware } from 'redux';
import createSagaMiddleware from 'redux-saga';
import thunk from 'redux-thunk';
import { put, takeEvery, delay, call, select } from 'redux-saga/effects';

const reducer = (state = 0, action) => {
  switch (action.type) {
    case 'put':
      return state + action.payload;
    default:
      return state;
  }
};
const sagaMiddleware = createSagaMiddleware()
export const store = createStore(reducer, applyMiddleware(sagaMiddleware, thunk));

function* main() {
  // 工具函数 delay 阻塞1s
  var start = Date.now();
  yield delay(1000);
  console.log(Date.now() - start);// 1秒多
  // put 类似于 dispatch
  yield put({ type: 'put' , payload:10});
  // takeEvery 不阻塞程序
  yield takeEvery('takeEvery111', function ({ type, payload }) {
    console.log('takeEvery', type, payload); // yield put({ type: 'takeEvery111' , payload:10}); 触发
  });
  // select 获取state中的数据
  const state = yield select((state) => state);
  console.log(state); // 10
  // call 阻塞程序
  yield call(function* () { // 阻塞
    yield delay(1000);
  });
  console.log(Date.now() - start);// 2秒多

  yield put({ type: 'takeEvery111' , payload:10});
}
sagaMiddleware.run(main);

依次打印出如下结果:

1001
10
2004
takeEvery takeEvery111 10
1. 自动执行Generator

从执行结果来看,这个main函数能自动按顺序执行说明在redux-saga的程序代码中有自动执行gen的机制,其实源码就是./internal/proc.js文件中,通过函数之间循环调用的方式执行这个gen函数。

这样的自动执行机制在generator中是比较常见的,比如co模块就具有这样的功能,其实现巧妙却不复杂,如下例子:

function makePromisify(source) {
    if (source.then && typeof source.then === "function") return source
    return Promise.resolve(source)
}

function run(generatorFunc) {
    let it = generatorFunc()
    let result = it.next()
    return new Promise((resolve, reject) => {
        const next = function (result) {
            if (result.done) {
                resolve(result.value)
            }
            //保证返回的是一个promise
            result.value = makePromisify(result.value)
            result.value.then(res => {
                //将promise的返回值res传入iterator迭代器的next方法中,作为yield后面表达式的返回值
                //it.next将停止的yield继续执行到下一个yield,返回的result是一个value,done属性组成的对象
                let result = it.next(res)
                //递归执行next函数
                next(result)
            }).catch(err => {
                reject(err)
            })
        }
        next(result)
    })
}
2. 发布订阅模式

我们看到takeEvery是可以拦截到{type: 'takeEvery111'}这个action,说明在redux-saga内部有类似发布订阅on/trigger这样的机制,通过阅读源码我们发现,内部通过channel这个东西来做发布订阅的处理;在./internal/channel.js就有这样的源码:

put(input) {
  const takers = (currentTakers = nextTakers)
  for (let i = 0, len = takers.length; i < len; i++) {
    const taker = takers[i]
    // 如果take匹配到, 执行它
    if (taker[MATCH](input)) {
      taker.cancel()
      taker(input) // 发布
    }
  }
},
take(cb, matcher = matchers.wildcard) {
  cb[MATCH] = matcher
  ensureCanMutateNextTakers()
  nextTakers.push(cb) // 订阅
},
3. put, takeEvery, delay, call返回effect

put执行并不是直接dispatch一个action,而是通过yield向redux-saga内部传递参数(这个参数在redux-saga中叫effect),内部根据这个参数决定具体的执行内容。

在源码中put、fork、call 等是通过 makeEffect 创建了一系列 effect,这个 effect 是一个普通的 js 对象,上面挂载了一些相关的信息,并且把这个effect yield到内部的runEffejct中,然后根据type字段决定真正需要执行的程序过程。

const makeEffect = (type, payload) => ({
  [IO]: true,
  combinator: false,
  type,
  payload,
});

总结

redux-saga还有许多要探索的地方,比如channel、状态机的应用、任务队列,等搞明白之后再来分享。

与redux-saga原理相似的内容:

redux-saga原理

前言 工作中使用了redux-saga这个redux中间件,如果不明白内部原理使用起来会让人摸不着头脑,阅读源码后特意对其原理做下总结。 redux的特点 一个标准、管理应用副作用的redux中间件 实现切面编程方式 声明式的编写方式 订阅发布的设计模式 优点: 把异步操作转移到单独 saga文件中

Redux 的困扰与如何技术选型

文章的名字我想了很久,备选项有“我再不推荐 Redux”,“Redux 为什么令我头疼”,“Redux 进化启示录”等等。通过这一系列名字我想你大概能猜到我接下来想聊的问题是什么

redux原理分享

概述 一个状态管理工具 Store:保存数据的地方,你可以把它看成一个容器,整个应用只能有一个 Store。 State:包含所有数据,如果想得到某个时点的数据,就要对 Store 生成快照,这种时点的数据集合,就叫做 State。 Action:Action 就是 View 发出的通知,表示 St

Redux与前端表格施展“组合拳”,实现大屏展示应用的交互增强

本文由葡萄城技术团队于博客园原创并首发 转载请注明出处:葡萄城官网,葡萄城为开发者提供专业的开发工具、解决方案和服务,赋能开发者。 Redux 是 JavaScript 状态容器,提供可预测化的状态管理。它可以用在 react、angular、vue 等项目中, 但与 react 配合使用更加方便一

如何提高redux开发效率?当然是redux-tookit啦!

前言 使用react-redux的朋友都经历过这种痛苦吧? 定义一个store仓库,首先创建各种文件,比如reducer、action、store...,然后 将redux和react连接使用。整个流程繁琐,写起来代码冗余。 react-redux创建仓库,文件目录如下: 好怀念使用 vuex创建写

[转帖]【文章导读】什么是旁道攻击?Meltdown Redux英特尔漏洞(MDS攻击);KAISER:从用户空间隐藏内核(KAISER);Meltdown/Spectre分析

Table of Contents 黑客词典:什么是旁道攻击? Meltdown Redux:Intel缺陷使黑客窃取了数百万台PC的秘密 三重熔毁:有多少研究人员同时发现了20年的芯片缺陷 KAISER:从用户空间隐藏内核 迟到的Meltdown/Spectre分析 黑客词典:什么是旁道攻击? h

vue3 快速入门系列 —— 状态管理 pinia

其他章节请看: vue3 快速入门 系列 Pinia vue3 状态管理这里选择 pinia。 虽然 vuex4 已支持 Vue 3 的 Composition API,但是 vue3 官网推荐新的应用使用 pinia —— vue3 pinia 集中式状态管理 redux、mobx、vuex、pi

认识一下 Mobx

我们是袋鼠云数栈 UED 团队,致力于打造优秀的一站式数据中台产品。我们始终保持工匠精神,探索前端道路,为社区积累并传播经验价值。 本文作者:霜序(LuckyFBB) 前言 在之前的文章中,我们讲述了 React 的数据流管理,从 props → context → Redux,以及 Redux 相