超级易懂的redux-saga原理解析

2023-12-18 17:58

本文主要是介绍超级易懂的redux-saga原理解析,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

原文地址

前言

笔者最近在做一些后台项目,使用的是Ant Design Pro,其使用了redux-saga处理异步数据流,本文将对redux-saga的原理做一个简单的解读,并将实现一个简易版的redux-saga。

Generator函数的自动流程控制

在redux-saga中,saga是指一些长时操作,用generator函数表示。generator函数的强大之处在于其可以手动的暂停、恢复执行,且可以与函数体外进行数据交互,看如下例子:

function *gen() {const a = yield 'hello';console.log(a);
}cont g = gen();
g.next(); // { value: 'hello', done: false }
setTimeout(() => g.next('hi'), 1000)  // 此时 a => 'hi'   一秒后打印‘hi'
复制代码

可以看出来genrator函数何时进行下一步操作完全取决于外部的调度时机,且其内部执行状态也由外部的输入决定,这使得generator函数可以很方便的做异步流程控制。举个例子,我们首先读取一个文件的内容作为查询参数,然后请求一个查询接口并把返回的内容打印出来:

function getParams(file) {return new Promise(resolve => {fs.readFile(file, (err, data) => {resolve(data)})})
}function getContent(params) {//  request返回promisereturn request(params)
}function *gen() {const params = yield getParams('config.json');const content = yield getContent(params);console.log(content);
}
复制代码

我们可以手动控制gen函数的执行:

const g = gen();
g.next().value.then(params => {g.next(params).value.then(content => {g.next(content);})
})
复制代码

以上可以达到我们的目的,但是过于繁琐,我们想要的是generator函数可以自动的执行,可以写一个简易的自动执行函数如下:

function genRun(gen) {const g = gen();next();function next(err, pre) {let temp;(err === null) && (temp = g.next(pre));(err !== null) && (temp = g.throw(pre));if(!temp.done) {nextWithYieldType(temp.value, next);}}
}function nextWithYieldType(value, next) {if(isPromise(value)) {value.then(success => next(null, success)).catch(error => next(error))} 
}genRun(gen);
复制代码

此时generator函数便可以自动执行,事实上我们可以发现,generator的内部状态完全是由nextWithYieldType决定的,我们可以根据yield的类型执行不同的处理逻辑。

Effect

事实上sagaMiddleware.run(saga)可以类似看做genRun(saga),而saga是由一个个的effect组成的,那么effect是什么?redux-saga官网的解释:一个 effect 就是一个 Plain Object JavaScript 对象,包含一些将被 saga middleware 执行的指令。redux-saga提供了很多Effect创建器,如callputtake等,已call为例:

function saga*() {const result = yield call(genPromise);console.log(result);
}
复制代码

call(genPromise)生成的就是一个effect,它可能类似如下:

{isEffect: true,type: 'CALL',fn: genPromise
}
复制代码

事实上effect只表明了意图,而实际的行为由类似于上文的nextWithYieldType完成,例如:

function nextWithYieldType(value, next) {...if(isCallEffect(value)) {value.fn(). then(success => next(null, success)).catch(error => next(error))  } 
}
复制代码

当genPromise函数返回的promise被resolve后便会打印出结果。

生产者与消费者

观察下面的例子

function *saga() {yield take('TEST');console.log('test...');
}sagaMiddleware.run(test);
复制代码

saga会在take('TEST')处阻塞,只有执行了dispatch({type: 'TEST'})后saga才能继续运行(注意:此时的dispatch方法是经过sagaMiddleware包装过的)。这给我们的感觉似乎很像是take是一个生产者,在等待disaptch的消费,事实上take只是一个Effect生成器,具体的处理逻辑依然是在nextWithYieldType完成的,类似于:

function nextWithYieldType(value, next) {...// take('TEST')生成的effect简单的认为是  {isEffect: true, type: 'TAKE', name: 'TEST'}if(isTakeEffect(value)) {channel.take({pattern: value.name, cb: params => next(null, params)})  } 
}
复制代码

channel是一个任务生成器,它有两个方法:take生成任务,put消费任务:

function channel() {/*task = {pattern,cb}*/let _task = null;function take(task) {_task = task;}function put(pattern, args) {if(!_task) return;if(pattern == _task.pattern) _task.cb.call(null, args);}return {take,put}
}
复制代码

显然任务是在执行dispatch的时候被消费掉的,这个工作是在sagaMiddleware中做的,类似于如下:

const sagaMiddleware = store => {return next => action => {next(action);const { type, ...payload } = action;channel.put(type, payload);}
} 
复制代码

看到这里我们可以发现,需要我们做的就是不断的完善nextWithYieldType这个函数,当完成了putforktakeEvery对应的逻辑后,一个具备基本功能的redux-saga就诞生啦,笔者就不在赘述这些功能的实现了。最后,你可以查看这里:tiny-redux-saga,这是笔者实现的一个简易版的redux-saga,希望对你有所帮助。


全文完。


作者:菜菜_张
链接:https://juejin.im/post/5bd5bd8bf265da0aca33544d
来源:掘金
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

这篇关于超级易懂的redux-saga原理解析的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/509291

相关文章

网页解析 lxml 库--实战

lxml库使用流程 lxml 是 Python 的第三方解析库,完全使用 Python 语言编写,它对 XPath表达式提供了良好的支 持,因此能够了高效地解析 HTML/XML 文档。本节讲解如何通过 lxml 库解析 HTML 文档。 pip install lxml lxm| 库提供了一个 etree 模块,该模块专门用来解析 HTML/XML 文档,下面来介绍一下 lxml 库

深入探索协同过滤:从原理到推荐模块案例

文章目录 前言一、协同过滤1. 基于用户的协同过滤(UserCF)2. 基于物品的协同过滤(ItemCF)3. 相似度计算方法 二、相似度计算方法1. 欧氏距离2. 皮尔逊相关系数3. 杰卡德相似系数4. 余弦相似度 三、推荐模块案例1.基于文章的协同过滤推荐功能2.基于用户的协同过滤推荐功能 前言     在信息过载的时代,推荐系统成为连接用户与内容的桥梁。本文聚焦于

【C++】_list常用方法解析及模拟实现

相信自己的力量,只要对自己始终保持信心,尽自己最大努力去完成任何事,就算事情最终结果是失败了,努力了也不留遗憾。💓💓💓 目录   ✨说在前面 🍋知识点一:什么是list? •🌰1.list的定义 •🌰2.list的基本特性 •🌰3.常用接口介绍 🍋知识点二:list常用接口 •🌰1.默认成员函数 🔥构造函数(⭐) 🔥析构函数 •🌰2.list对象

hdu4407(容斥原理)

题意:给一串数字1,2,......n,两个操作:1、修改第k个数字,2、查询区间[l,r]中与n互质的数之和。 解题思路:咱一看,像线段树,但是如果用线段树做,那么每个区间一定要记录所有的素因子,这样会超内存。然后我就做不来了。后来看了题解,原来是用容斥原理来做的。还记得这道题目吗?求区间[1,r]中与p互质的数的个数,如果不会的话就先去做那题吧。现在这题是求区间[l,r]中与n互质的数的和

hdu4407容斥原理

题意: 有一个元素为 1~n 的数列{An},有2种操作(1000次): 1、求某段区间 [a,b] 中与 p 互质的数的和。 2、将数列中某个位置元素的值改变。 import java.io.BufferedInputStream;import java.io.BufferedReader;import java.io.IOException;import java.io.Inpu

hdu4059容斥原理

求1-n中与n互质的数的4次方之和 import java.io.BufferedInputStream;import java.io.BufferedReader;import java.io.IOException;import java.io.InputStream;import java.io.InputStreamReader;import java.io.PrintWrit

OWASP十大安全漏洞解析

OWASP(开放式Web应用程序安全项目)发布的“十大安全漏洞”列表是Web应用程序安全领域的权威指南,它总结了Web应用程序中最常见、最危险的安全隐患。以下是对OWASP十大安全漏洞的详细解析: 1. 注入漏洞(Injection) 描述:攻击者通过在应用程序的输入数据中插入恶意代码,从而控制应用程序的行为。常见的注入类型包括SQL注入、OS命令注入、LDAP注入等。 影响:可能导致数据泄

从状态管理到性能优化:全面解析 Android Compose

文章目录 引言一、Android Compose基本概念1.1 什么是Android Compose?1.2 Compose的优势1.3 如何在项目中使用Compose 二、Compose中的状态管理2.1 状态管理的重要性2.2 Compose中的状态和数据流2.3 使用State和MutableState处理状态2.4 通过ViewModel进行状态管理 三、Compose中的列表和滚动

Spring 源码解读:自定义实现Bean定义的注册与解析

引言 在Spring框架中,Bean的注册与解析是整个依赖注入流程的核心步骤。通过Bean定义,Spring容器知道如何创建、配置和管理每个Bean实例。本篇文章将通过实现一个简化版的Bean定义注册与解析机制,帮助你理解Spring框架背后的设计逻辑。我们还将对比Spring中的BeanDefinition和BeanDefinitionRegistry,以全面掌握Bean注册和解析的核心原理。

CSP 2023 提高级第一轮 CSP-S 2023初试题 完善程序第二题解析 未完

一、题目阅读 (最大值之和)给定整数序列 a0,⋯,an−1,求该序列所有非空连续子序列的最大值之和。上述参数满足 1≤n≤105 和 1≤ai≤108。 一个序列的非空连续子序列可以用两个下标 ll 和 rr(其中0≤l≤r<n0≤l≤r<n)表示,对应的序列为 al,al+1,⋯,ar​。两个非空连续子序列不同,当且仅当下标不同。 例如,当原序列为 [1,2,1,2] 时,要计算子序列 [