Redus-saga是一个redux的中间件,主要用来简便而优雅的处理redux应用里的副作用(side effect相对于pure function这类概念而言的)。它之所以可以做到这一点主要是使用了ES6里的一个语法:Generator。使用Generator可以像写同步的代码一样编写异步代码,这样更加容易测试。

在我们更深入之前,“saga”这个名字在计算机科学的历史上早已存在,并不是只用于javascript里的。Saga可以简要的概括为一种处理长时间运行的事务,并且这种事务会有副作用或者失败的可能。每一个我们希望完成的事务,都需要一个反事务在出错的时候把事务恢复到当前事务发生之前的样子。如果有兴趣了解更多Sage,我(作者)推荐你看看Caitie McCaffrey的这段演讲,演讲的题目是《实践Saga模式》。另外可以参阅Roman Liutikov的博客,叫做《Saga模式的迷惑之处》

我们为什么要用Redux-saga

现在我们可以创建一个新的react-redux应用,我们会使用redux-thunk和redux-saga处理异步的action。所以我们为什么要用redux-saga呢?

正如文档里所说:

与redux-thunk相反,你不会跌入回调地狱,你可以很容易的测试你的异步流程而且action一直保持pure(纯的状态)。

我们来对比一下saga和thunk的用法来更深入的理解上面那句话的内容。假设场景为:用户点击按钮,发出一个http请求来获取数据。

Redux thunk的写法:

import {
API_BUTTON_CLICK,
API_BUTTON_CLICK_SUCCESS,
API_BUTTON_CLICK_ERROR,
} from './actions/consts';
import { getDataFromAPI } from './api'; const getDataStarted = () => ({type: API_BUTTON_CLICK});
const getDataSuccess = data => ({type: API_BUTTON_CLICK_SUCESS, payload: data});
const getDataError = message => ({type: API_BUTTON_CLICK_ERROR, payload: message}); const getDataFromAPI = () => {
return dispatch => {
dispatch(getDataStarted()); getDataFromAPI()
.then(data => {
dispatch(getUserSucess(data));
}).fail(err => {
dispatch(getDataError(err.message));
})
}
}

这里我们有一个叫做getDataFromAPI()的方法来创建action。当用户点击按钮开始了异步流程的时候“

  • 首先触发一个action让我们的store知道我们要发起一个异步请求(dispatch(getDataStarted())了。
  • 接着我们实际发出了API请求,这个请求会返回一个promise。
  • 然后我们会在成功接受到请求数据的时候触发成功的action,有错误的时候触发错误的action。

Redux saga的写法:

import { call, put, takeEvery } from 'redux-saga/effects';
import {
API_BUTTON_CLICK,
API_BUTTON_CLICK_SUCCESS,
API_BUTTON_CLICK_ERROR,
} from './actoins/consts';
import { getDataFromAPI } from './api'; export function* apiSideEffect(action) {
try{
const data = yield call(getDataFromaAPI);
yield put({ type: API_BUTTON_CLICK_SUCESS, payload: data});
} catch(e) {
yield put({type: API_BUTTON_CLICK_ERROR, payload: e.message});
}
} // the 'watcher' -on every `API_BUTTON_CLICK` action, run our side effect
export function* apiSaga() {
yield takeEvery(API_BUTTON_CLICK, apiSideEffect);
}

流程基本上都是一样的,只是代码看起来略有不同:

  • 与thunk例子不同的是,我们没有使用dispatch而是用了put(我们可以认为两者是等价的)。
  • 我们有一个监听方法一直监听着“start”方法。它只会在按钮点击之后触发一个类型为API_BUTTON_CLICK的redux action。
  • 我们用redux-saga的call effect(专有名词,效果的意思。与side effect的effect同意)来从异步的方法(promise,不同的saga等)里面获取数据。

非常简单,对吧。在某些按钮的点击事件里,我们会请求某些终端来获取数据。如果成功了,就发起一个新的,payload就是数据的action。否则就发出一个带有error消息的action。

同时,需要注意认真的理解上面的例子非常重要。不是说thunk的例子难以理解,但是我们却摆脱了返回方法或者promise链的麻烦。我们还可以只简单用一个try-catch来处理任何的异步错误。然后put(或者dispatch)一个action来通知reducer。

其次,更重要的是,我们的saga side effect(saga副作用)是纯的(pure)。这是因为call(getDataFromAPI)并不实际执行API请求,它只是返回一个纯对象:{type: 'CALL', func, args}。实际的请求已经由redux-saga中间件执行,并且会把返回值带到generator里(所以需要用yeild关键字)或者抛出一个异常,如果有的话。

掌握了以上概念以后,你就会明白下面的测试为什么这么简单:

import { call, put } from 'redux-saga/effects';

import { API_BUTTON_CLICK_SUCCESS, } from './actions/consts';
import { getDataFromAPI } from './api'; it('apiSideEffect - fetches data from API and dispatches a success action', () => {
const generator = apiSideEffect(); expect(generator.next().value).toEqual(call(getDataFromAPI)); expect(generator.next().value).toEqual(put({type: API_BUTTON_CLICK_SUCCESS })); expect(generator.next()).toEqual({done: true, value: undefined});
});

然而上面的测试代码有一点点小问题。我们需要模拟getDataFromAPI方法的调用,其他在上面语句块的方法也需要模拟出来。这也许不是什么大的工作量,但是随着我们的action数量的增长,复杂度也会增加,类似上面的测试最好避免。

本文希望讲清楚redux-saga三个要点。我们不会再遇到回调地狱,我们的action是纯的,而且我们的异步流程很容易测试。如果你要学到更多,下面的资源会有帮助:

原文地址:https://engineering.universe.com/what-is-redux-saga-c1252fc2f4d1

初识Redux-Saga的更多相关文章

  1. [React] 14 - Redux: Redux Saga

    Ref: Build Real App with React #14: Redux Saga Ref: 聊一聊 redux 异步流之 redux-saga  [入门] Ref: 从redux-thun ...

  2. react系列(六)Redux Saga

    在Redux中常要管理异步操作,目前社区流行的有Redux-Saga.Redux-thunk等.在管理复杂应用时,推荐使用Redux-Saga,它提供了用 generator 书写类同步代码的能力. ...

  3. react native redux saga增加日志功能

    redux-logger地址:https://github.com/evgenyrodionov/redux-logger 目前Reac native项目中已经使用redux功能,异步中间件使用red ...

  4. redux+saga+reducer

    saga.js这个文件里面的函数实际没有在其他jsx中引用吧?这个文件的作用就是把异步数据拿到,放进reducer,如果jsx想取,需要结合connect来取数据.

  5. 初识redux走向redux-react

    废话不多说,先上一张图 首先记住redux设计思想 Web应用是一个转态机,视图与转态是一一对应的 所有的转态,保存在一个对象里 1.store 存储数据的容器,整个应用只有一个state,Redux ...

  6. 初识Redux Middleware

    前言 原先改变store是通过dispatch(action) = > reducer:那Redux的Middleware是什么呢?就是dispatch(action) = > reduc ...

  7. redux saga学习

    来源地址:https://www.youtube.com/watch?v=o3A9EvMspig Saga的基本写法 takeEvery与takeLatest的区别 takeEvery是指响应每一个请 ...

  8. [转] How to dispatch a Redux action with a timeout?

    How to dispatch a Redux action with a timeout? Q I have an action that updates notification state of ...

  9. 《React与Redux开发实例精解》读书笔记

    第五章 JSX语法 class属性改为className for属性改为htmlFor jsx中javascript表达式必须要有返回值,使用三元操作符 所有的标签必须闭合 input img等 re ...

随机推荐

  1. MySQL主从复制 + Mycat实现读写分离

    说明:两台MySQL服务器都是使用CentOS6.5系统,MySQL版本为mysql-5.7.17 MySQL一主一被实现主从复制 注意:写包括insert,delete,update 操作:读只有s ...

  2. PHP二维关联数组的遍历方式

    采用foreach循环对二维索引数组进行遍历,相对来讲速度更快,效率更高,foreach循环是PHP中专门用来循环数组的.实例也相对简单,多加练习,想清楚程序运行逻辑即可. <?php $arr ...

  3. 接口自动化测试方案PHP + mysql

    接口测试在测试工作中是很常见的工作,但是在以往的接口测试工作中借助的一般是第三方插件.python开发的发送请求脚本.LR脚本.Jmeter脚本,之前也使用python开发了一套接口自动化测试系统,但 ...

  4. 简说chart2.4的应用,以及Uncaught ReferenceError : require is not defined的解决

    51呢最近在学习chart.js,然后呢就照着中文的帮助文档来然后就一直出Uncaught ReferenceError : require is not defined的问题查了挺多才知道是帮助文档 ...

  5. gcd,最大公约数,lcm,最小公倍数

    int gcd(int a,int b){ ?a:gcd(b,a%b); } 关于lcm,若写成a*b/gcd(a,b) ,a*b可能会溢出! int lcm(int a,int b){ return ...

  6. asp.net C# 实现微信接口权限开发类

    当前微信接口类已实现以下接口,代码上如果不够简洁的,请自行处理. 1.获取access_token 2.获取用户基本信息 3.生成带参数二维码 4.新增永久素材 5.新增临时素材 6.发送微信模版 7 ...

  7. VS2017生成解决方案报错,提示对路径的访问被拒绝

    目前我用的vs2017的版本是15.3.5.生成解决方案有时会提示如下: 开始以为是权限的问题,找到相应的目录设置everyone权限,再次生成还是不行.重启VS试了下,还是不行. 最后无奈重启下电脑 ...

  8. 100. Same Tree(leetcode)

    Given two binary trees, write a function to check if they are equal or not. Two binary trees are con ...

  9. iOS之 Auto Layout

    1. 动画 // 修改从 StoryBoard 绑定到类的约束的值 self.boxView.constant += 80 // 在动画闭包里对其父级进行 layoutIfNeeded() UIVie ...

  10. Python 之简单线程池创建

    try: from Queue import Queue, Empty except: from queue import Queue, Empty import threading import t ...