Redux-saga学习笔记
概述
Redux-saga在Redux应用中扮演’中间件’的角色,主要用来执行数据流中的异步操作。主要通过ES6中的generator函数和yield关键字来以同步的方式实现异步操作。
基本用法:
- 使用createSagaMiddleware方法创建saga 的Middleware,然后在创建的redux的store时,使用applyMiddleware函数将创建的saga Middleware实例绑定到store上,最后可以调用saga Middleware的run函数来执行某个或者某些Middleware。
- 在saga的Middleware中,可以使用takeEvery或者takeLatest等API来监听某个action,当某个action触发后,saga可以使用call、fetch等api发起异步操作,操作完成后使用put函数触发action,同步更新state,从而完成整个State的更新。
API
- takeEvery
用来监听action,每个action都触发一次,如果其对应是异步操作的话,每次都发起异步请求,而不论上次的请求是否返回。
1
2
3
4
5
|
import { takeEvery } from 'redux-saga/effects' function * watchFetchData() { yield takeEvery( 'FETCH_REQUESTED' , fetchData) } |
- takeLatest
作用同takeEvery一样,唯一的区别是它只关注最后,也就是最近一次发起的异步请求,如果上次请求还未返回,则会被取消。
1
2
3
|
function * watchFetchData() { yield takeLatest( 'FETCH_REQUESTED' , fetchData) } |
- call
call用来调用异步函数,将异步函数和函数参数作为call函数的参数传入,返回一个js对象。saga引入他的主要作用是方便测试,同时也能让我们的代码更加规范化。
同js原生的call一样,call函数也可以指定this对象,只要把this对象当第一个参数传入call方法就好了
saga同样提供apply函数,作用同call一样,参数形式同js原生apply方法。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
|
import { call } from 'redux-saga/effects' function * fetchProducts() { const products = yield call(Api.fetch, '/products' ) // ... } import { call } from 'redux-saga/effects' import Api from '...' const iterator = fetchProducts() // expects a call instruction assert.deepEqual( iterator.next().value, call(Api.fetch, '/products' ), "fetchProducts should yield an Effect call(Api.fetch, './products')" ) yield call([obj, obj.method], arg1, arg2, ...) yield apply(obj, obj.method, [arg1, arg2, ...]) |
- cps
同call方法基本一样,但是用处不太一样,call一般用来完成异步操作,cps可以用来完成耗时比较长的io操作等。
- put
put是saga对Redux中dispatch方法的一个封装,调用put方法后,saga内部会分发action通知Store更新state。
这个借口主要也是为了方便我们写单元测试提供的。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
|
import { call, put } from 'redux-saga/effects' // ... function * fetchProducts() { const products = yield call(Api.fetch, '/products' ) // create and yield a dispatch Effect yield put({ type: 'PRODUCTS_RECEIVED' , products }) } const products = {} // expects a dispatch instruction assert.deepEqual( iterator.next(products).value, put({ type: 'PRODUCTS_RECEIVED' , products }), "fetchProducts should yield an Effect put({ type: 'PRODUCTS_RECEIVED', products })" ) |
- 请求失败
有两种方式来处理请求失败的情况,一种是使用try-catch方法,将错误抛出;另一种是使用变量缓存成功失败的状态。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
|
import Api from './path/to/api' import { call, put } from 'redux-saga/effects' // ... function * fetchProducts() { try { const products = yield call(Api.fetch, '/products' ) yield put({ type: 'PRODUCTS_RECEIVED' , products }) } catch (error) { yield put({ type: 'PRODUCTS_REQUEST_FAILED' , error }) } } import Api from './path/to/api' import { call, put } from 'redux-saga/effects' function fetchProductsApi() { return Api.fetch( '/products' ) .then(response => ({ response })) . catch (error => ({ error })) } function * fetchProducts() { const { response, error } = yield call(fetchProductsApi) if (response) yield put({ type: 'PRODUCTS_RECEIVED' , products: response }) else yield put({ type: 'PRODUCTS_REQUEST_FAILED' , error }) } |
- take
take的表现同takeEvery一样,都是监听某个action,但与takeEvery不同的是,他不是每次action触发的时候都相应,而只是在执行顺序执行到take语句时才会相应action。
当在genetator中使用take语句等待action时,generator被阻塞,等待action被分发,然后继续往下执行。
takeEvery只是监听每个action,然后执行处理函数。对于何时相应action和 如何相应action,takeEvery并没有控制权。
而take则不一样,我们可以在generator函数中决定何时相应一个action,以及一个action被触发后做什么操作。
最大区别:take只有在执行流达到时才会响应对应的action,而takeEvery则一经注册,都会响应action。
1
2
3
4
5
6
7
8
|
import { take, put } from 'redux-saga/effects' function * watchFirstThreeTodosCreation() { for ( let i = 0; i < 3; i++) { const action = yield take( 'TODO_CREATED' ) } yield put({type: 'SHOW_CONGRATULATION' }) } |
- fork
非阻塞任务调用机制:上面我们介绍过call可以用来发起异步操作,但是相对于generator函数来说,call操作是阻塞的,只有等promise回来后才能继续执行,而fork是非阻塞的 ,当调用fork启动一个任务时,该任务在后台继续执行,从而使得我们的执行流能继续往下执行而不必一定要等待返回。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
|
import { take, call, put, cancelled } from 'redux-saga/effects' import Api from '...' function * authorize(user, password) { try { const token = yield call(Api.authorize, user, password) yield put({type: 'LOGIN_SUCCESS' , token}) yield call(Api.storeItem, {token}) return token } catch (error) { yield put({type: 'LOGIN_ERROR' , error}) } finally { if ( yield cancelled()) { // ... put special cancellation handling code here } } } |
- cancel
cancel的作用是用来取消一个还未返回的fork任务。防止fork的任务等待时间太长或者其他逻辑错误。
- all
all提供了一种并行执行异步请求的方式。之前介绍过执行异步请求的api中,大都是阻塞执行,只有当一个call操作放回后,才能执行下一个call操作, call提供了一种类似Promise中的all操作,可以将多个异步操作作为参数参入all函数中,如果有一个call操作失败或者所有call操作都成功返回,则本次all操作执行完毕。
1
2
3
4
5
6
7
|
import { all, call } from 'redux-saga/effects' // correct, effects will get executed in parallel const [users, repos] = yield all([ call(fetch, '/users' ), call(fetch, '/repos' ) ]) |
- race
有时候当我们并行的发起多个异步操作时,我们并不一定需要等待所有操作完成,而只需要有一个操作完成就可以继续执行流。这就是race借口的用处。他可以并行的启动多个异步请求,只要有一个 请求返回(resolved或者reject),race操作接受正常返回的请求,并且将剩余的请求取消。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
|
import { race, take, put } from 'redux-saga/effects' function * backgroundTask() { while ( true ) { ... } } function * watchStartBackgroundTask() { while ( true ) { yield take( 'START_BACKGROUND_TASK' ) yield race({ task: call(backgroundTask), cancel: take( 'CANCEL_TASK' ) }) } } |
- actionChannel
在之前的操作中,所有的action分发是顺序的,但是对action的响应是由异步任务来完成,也即是说对action的处理是无序的。
如果需要对action的有序处理的话,可以使用actionChannel函数来创建一个action的缓存队列,但一个action的任务流程处理完成后,才可是执行下一个任务流。
代码参考:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
import { take, actionChannel, call, ... } from 'redux-saga/effects' function * watchRequests() { // 1- Create a channel for request actions const requestChan = yield actionChannel( 'REQUEST' ) while ( true ) { // 2- take from the channel const {payload} = yield take(requestChan) // 3- Note that we're using a blocking call yield call(handleRequest, payload) } } function * handleRequest(payload) { ... } |
- eventChannel
Throttling
用来防止连续不断的响应某个事件。
1
2
3
4
5
6
7
8
9
|
import { throttle } from 'redux-saga/effects' function * handleInput(input) { // ... } function * watchInput() { yield throttle(500, 'INPUT_CHANGED' , handleInput) } |
Debouncing
延时执行,使用delay函数实现
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
|
import { delay } from 'redux-saga' function * handleInput(input) { // debounce by 500ms yield call(delay, 500) ... } function * watchInput() { let task while ( true ) { const { input } = yield take( 'INPUT_CHANGED' ) if (task) { yield cancel(task) } task = yield fork(handleInput, input) } } const delay = (ms) => new Promise(resolve => setTimeout(resolve, ms)) |
参考:https://redux-saga.js.org/docs/api/
Redux-saga学习笔记的更多相关文章
- 【原】redux异步操作学习笔记
摘要: 发觉在学习react的生态链中,react+react-router+webpack+es6+fetch等等这些都基本搞懂的差不多了,可以应用到实战当中,唯独这个redux还不能,学习redu ...
- redux saga学习
来源地址:https://www.youtube.com/watch?v=o3A9EvMspig Saga的基本写法 takeEvery与takeLatest的区别 takeEvery是指响应每一个请 ...
- React+Redux学习笔记:React+Redux简易开发步骤
前言 React+Redux 分为两部分: UI组件:即React组件,也叫用户自定义UI组件,用于渲染DOM 容器组件:即Redux逻辑,处理数据和业务逻辑,支持所有Redux API,参考之前的文 ...
- udacity android 学习笔记: lesson 4 part b
udacity android 学习笔记: lesson 4 part b 作者:干货店打杂的 /titer1 /Archimedes 出处:https://code.csdn.net/titer1 ...
- redux-form的学习笔记
redux是一种常用的与react框架搭配的一种数据流架构,而伴随着redux的出现,也出现了许多基于redux开源的第三方库,而redux-form就是其中之一的开源组件库,到今天我写下这篇笔记为止 ...
- The Road to learn React书籍学习笔记(第二章)
The Road to learn React书籍学习笔记(第二章) 组件的内部状态 组件的内部状态也称为局部状态,允许保存.修改和删除在组件内部的属性,使用ES6类组件可以在构造函数中初始化组件的状 ...
- The Road to learn React书籍学习笔记(第三章)
The Road to learn React书籍学习笔记(第三章) 代码详情 声明周期方法 通过之前的学习,可以了解到ES6 类组件中的生命周期方法 constructor() 和 render() ...
- js学习笔记:webpack基础入门(一)
之前听说过webpack,今天想正式的接触一下,先跟着webpack的官方用户指南走: 在这里有: 如何安装webpack 如何使用webpack 如何使用loader 如何使用webpack的开发者 ...
- PHP-自定义模板-学习笔记
1. 开始 这几天,看了李炎恢老师的<PHP第二季度视频>中的“章节7:创建TPL自定义模板”,做一个学习笔记,通过绘制架构图.UML类图和思维导图,来对加深理解. 2. 整体架构图 ...
- PHP-会员登录与注册例子解析-学习笔记
1.开始 最近开始学习李炎恢老师的<PHP第二季度视频>中的“章节5:使用OOP注册会员”,做一个学习笔记,通过绘制基本页面流程和UML类图,来对加深理解. 2.基本页面流程 3.通过UM ...
随机推荐
- 链家2018春招C/C++开发实习生在线考试编程题
题目一 题解:该题目意思就是让你输入n组数据,然后求并集,利用STL容器set集合的特性:元素不重复存储,我们可以很轻易得出答案 #include <iostream> #include ...
- 机器学习 Python实践-K近邻算法
机器学习K近邻算法的实现主要是参考<机器学习实战>这本书. 一.K近邻(KNN)算法 K最近邻(k-Nearest Neighbour,KNN)分类算法,理解的思路是:如果一个样本在特征空 ...
- thinkphp模型创建
- IntelliJ IDEA + Tomcat ;On Upate Action 与 On Frame Deactivation
On Upate Action 与 On Frame Deactivation 这两个选项的设置,依赖于 项目的部署方式 是war包 还是 exploded ,看下面的gif: 这里实在是太灵活了, ...
- Linux下LAMP服务器的搭建
1.安装并配置Apache 安装apache的方法有很多种,这里选择通过yum方式进行安装,但需要Linux系统能够连接互联网,执行如下命令,安装Apache. # yum install httpd ...
- cross apply 和 outer apply
使用APPLY运算符可以实现查询操作的外部表表达式返回的每个调用表值函数.表值函数作为右输入,外部表表达式作为左输入. 通过对右输入求值来获得左输入每一行的计算结果,生成的行被组合起来作为最终输出.A ...
- RCTF2015 pwn试题分析
pwn200 漏洞给的很明显,先是读到了main的局部数组中,然后在子函数中向子函数的局部数组栈里复制. 总体思路是leak system的地址,然后再向一个固定地址写入/bin/sh,最后执行sys ...
- chmod g+s 、chmod o+t 、chmod u+s:Linux高级权限管理
关于linux下权限操作chmod的一些说明!比rxw高级内容! 转载自http://blog.chinaunix.net/uid-26642180-id-3378119.html Set uid, ...
- 关于 eclipse启动卡死的问题 解决方法
关于 eclipse启动卡死的问题(eclipse上一次没有正确关闭,导致启动的时候卡死错误解决方法),自己常用的解决方法: 方案一(推荐使用,如果没有这个文件,就使用方案二): 到<works ...
- pomelo 安装
1. 安装nodejs ,python ,C++运行环境(VS2012以上版本) 2.npm install -g node-gyp --registry=https://registry.npm.t ...