React开发进阶redux saga使用原理详解

目录
  • 前言
  • redux的特点
  • 分析原理
    • 1. 自动执行Generator
    • 2. 发布订阅模式
    • 3. put, takeEvery, delay, call返回effect
  • 总结

前言

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

redux的特点

  • 一个标准、管理应用副作用的redux中间件
  • 实现切面编程方式
  • 声明式的编写方式

订阅发布的设计模式

优点:

  • 把异步操作转移到单独 saga文件中,而不是糅杂在action或者component中;
  • dispatch的参数保持为纯粹的action而不是thunk function;
  • 大量的saga辅助函数和effect创建器减少了开发者的开发成本;
  • 灵活的串行或并行能够实现复杂的异步流程。

分析原理

先举个实践的例子

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、状态机的应用、任务队列,等。。。更多关于React进阶redux saga原理的资料请关注我们其它相关文章!

(0)

相关推荐

  • React不使用requestIdleCallback实现调度原理解析

    目录 1.起因 2.查找问题 3.解决问题 4.总结 5.吐槽 1.起因 最近在一边啃源码,一边手写fiber嘛,然后也看了很多博客和资料,基本上大伙好像都是说用requestIdleCallback来模拟react实现一个空闲时间调度.但我自己手写的时候把怎么用怎么怪,老是感觉有什么地方不对劲而且是在调度过程中,可能是因为我是想写出来来一个相对健全一点的模版方便我以后写源码的其他部分把,然后分析了一下所以有了这篇博客. 2.查找问题 1.requestIdleCallback是利用帧之间空闲时

  • React应用框架Dva数据流向原理总结分析

    目录 Dva是什么? 流程图怎么看? Model对象属性 数据流向 Dva是什么? 在刚刚接触Dva时,我最想知道的第一个问题就是: Dva官网文档的介绍是: dva 是体验技术部开发的 React 应用框架,将上面三个 React 工具库包装在一起,简化了 API,让开发 React 应用更加方便和快捷. dva = React-Router + Redux + Redux-saga 说实话这些名词让我只能一个一个的百度,虽然不能说毫无收获,但是依然难以理解. 现在我的理解Dva是一个轻量化的

  • React Streaming SSR原理示例深入解析

    目录 功能简介 基本原理 使用示例 Streaming HTML Selective Hydration 降级逻辑 JS 和 CSS 设置 源码解析 数据结构 Segment Boundary Task Request 主要流程 功能简介 React 18 提供了一种新的 SSR 渲染模式: Streaming SSR.通过 Streaming SSR,我们可以实现以下两个功能: Streaming HTML:服务端可以分段传输 HTML 到浏览器,而不是像 React 18 以前一样,需要等待

  • React redux 原理及使用详解

    目录 概述 createStore创建store applyMiddleware 应用中间件 combineReducers 合并多个reducer dispatch 中间件 中间件的调用顺序 store redux 数据流 bindActionCreators compose enhancer 使用 redux 常遇见的问题 概述 一个状态管理工具 Store:保存数据的地方,你可以把它看成一个容器,整个应用只能有一个 Store. State:包含所有数据,如果想得到某个时点的数据,就要对

  • React Context原理深入理解源码示例分析

    目录 正文 一.概念 二.使用 2.1.React.createContext 2.2.Context.Provider 2.3.React.useContext 2.4.Example 三.原理分析 3.1.createContext 函数实现 3.2. JSX 编译 3.3.消费组件 - useContext 函数实现 3.4.Context.Provider 在 Fiber 架构下的实现机制 3.5.小结 四.注意事项 五.对比 useSelector 正文 在 React 中提供了一种「

  • React Hydrate原理源码解析

    目录 引言 Demo ReactDOM.render ReactDOM.hydrate hydrate 过程 事件绑定 hydrate 源码剖析 beginWork HostRoot Fiber HostComponent HostText Fiber tryToClaimNextHydratableInstance completeUnitOfWork popHydrationState prepareToHydrateHostInstance prepareToHydrateHostText

  • React classnames原理及测试用例

    目录 前言 classnames 的用法 学会 classnames 的原理 测试用例的使用 总结 前言 本期的源码阅读任务是: 学会 classnames 的用法 学会 classnames 的原理 测试用例的使用 源码地址:JedWatson/classnames: A simple javascript utility for conditionally joining classNames together (github.com) classnames 的用法 Classname 是一

  • React开发进阶redux saga使用原理详解

    目录 前言 redux的特点 分析原理 1. 自动执行Generator 2. 发布订阅模式 3. put, takeEvery, delay, call返回effect 总结 前言 工作中使用了redux-saga这个redux中间件,如果不明白内部原理使用起来会让人摸不着头脑,阅读源码后特意对其原理做下总结. redux的特点 一个标准.管理应用副作用的redux中间件 实现切面编程方式 声明式的编写方式 订阅发布的设计模式 优点: 把异步操作转移到单独 saga文件中,而不是糅杂在acti

  • React immer与Redux Toolkit使用教程详解

    目录 1. immer 1.1 setState结合immer使用 1.2 useState结合immer使用 1.3 immer和redux集合 2. Redux Toolkit 1. immer 概述: 它和immutable相似的,实现了操作对象的数据共享,可以优化性能.它实现的原理使用es6的Proxy完成的.小巧,没有像immutable哪样有新数据类型,而是使用js类型. 安装: yarn add immer@9 1.1 setState结合immer使用 简单使用: import

  • React状态管理Redux的使用介绍详解

    目录 1. 简介 2. 核心概念 3. redux工作流 4. 模拟redux工作流程 5. 使用redux 6. react-redux 1. 简介 Redux 是 JavaScript 应用的状态容器(对象),提供可预测的状态管理.可以让你开发出行为稳定可预测的应用,运行于不同的环境(客户端.服务器.原生应用),并且易于测试.Redux 除了和 React 一起用外,还支持其它界面库. 解决的问题:多层级组件间通信问题. 2. 核心概念 单一数据源 整个redux中的数据都是集中管理,存储于

  • React为什么需要Scheduler调度器原理详解

    目录 正文 我们为什么需要Scheduler(调度器) Scheduler如何进行工作 总结 正文 最近在重学React,由于近两年没使用React突然重学发现一些很有意思的概念,首先便是React的Scheduler(调度器) 由于我对React的概念还停留在React 15之前(就是那个没有hooks的年代),所以接触Scheduler(调度器) 让我感觉很有意思: 在我印象中React的架构分为两层(React 16 之前) Reconciler(协调器)—— 负责找出变化的组件 Rend

  • IOS 开发之应用唤起实现原理详解

    一.什么是iOS应用唤起 IOS中的应用唤起用来实现以下功能:在浏览器中可以通过某些方式打开IOS手机本地的app,如果该app没有安装可以跳转到该应用对应的App Store的下载页. 二.App store下载页连接 App store中某个应用的下载页连接形如:https://itunes.apple.com/us/app/id399608199.在PC端浏览器打开该连接会跳转到应用详情页的PC端界面.在Safari中打开该连接,浏览器会询问是否在App Store中打开该连接,选择打开即

  • React Context源码实现原理详解

    目录 什么是 Context Context 使用示例 createContext Context 的设计非常特别 useContext useContext 相关源码 debugger 查看调用栈 什么是 Context 目前来看 Context 是一个非常强大但是很多时候不会直接使用的 api.大多数项目不会直接使用 createContext 然后向下面传递数据,而是采用第三方库(react-redux). 想想项目中是不是经常会用到 @connect(...)(Comp) 以及 <Pro

  • JSP动态网页开发原理详解

    一.什么是JSP?     JSP全称是Java Server Pages,它和servle技术一样,都是SUN公司定义的一种用于开发动态web资源的技术. JSP这门技术的最大的特点在于,写jsp就像在写html,但它相比html而言,html只能为用户提供静态数据,而Jsp技术允许在页面中嵌套java代码,为用户提供动态数据. 二.JSP原理 2.1.Web服务器是如何调用并执行一个jsp页面的? 浏览器向服务器发请求,不管访问的是什么资源,其实都是在访问Servlet,所以当访问一个jsp

  • java开发MVC三层架构上再加一层Manager层原理详解

    目录 MVC三层架构 MVC架构弊端 Manager层的特征 Manager层使用案例 MVC三层架构 我们在刚刚成为程序员的时候,就会被前辈们 "教育" 说系统的设计要遵循 MVC(Model-View-Controller)架构.它将整体的系统分成了 Model(模型),View(视图)和 Controller(控制器)三个层次,也就是将用户视图和业务处理隔离开,并且通过控制器连接起来,很好地实现了表现和逻辑的解耦,是一种标准的软件分层架构. MVC分层架构是架构上最简单的一种分层

  • 微服务架构设计RocketMQ进阶事务消息原理详解

    目录 前言 RocketMQ事务流程概要 RocketMQ事务流程关键 实现 基础配置 引入组件 添加配置 发送半消息 执行本地事务与回查 消费消息 测试 总结 前言 分布式消息选型的时候是否支持事务消息是一个很重要的考量点,而目前只有RocketMQ对事务消息支持的最好.今天我们来唠唠如何实现RocketMQ的事务消息! Apache RocketMQ在4.3.0版中已经支持分布式事务消息,这里RocketMQ采用了2PC的思想来实现了提交事务消息,同时增加一个补偿逻辑来处理二阶段超时或者失败

  • JavaScript开发简单易懂的Svelte实现原理详解

    目录 Demo1 create_fragment SvelteComponent 可以改变状态的Demo Svelte问世很久了,一直想写一篇好懂的原理分析文章,拖了这么久终于写了. Demo1 首先来看编译时,考虑如下App组件代码: <h1>{count}</h1> <script> let count = 0; </script> 这段代码经由编译器编译后产生如下代码,包括三部分: create_fragment方法 count的声明语句 class

随机推荐