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. SQL监测语句

    SELECT top 20 qs.creation_time,last_execution_time,total_physical_reads,total_logical_reads,total_lo ...

  2. Linux系统初始化过程及运行级别简介

    Linux开机过程: 1 开机自检(BIOS):初始化硬件,查找启动介质 2 MBR引导 3 GRUB引导菜单:GRUB程序安装在Bootloader 4 加载内核(kernel) 5 运行INIT进 ...

  3. break和continue 的区别

    区别 break和continue都可在循环语句里面使用,也都可以控制外层的循环.但是continue只能在循环语句里面使用,break也可以使用在switch语句里面. break具体作用在循环语句 ...

  4. 当谈到 GitLab CI 的时候,我们该聊些什么(上篇)

    "微服务"这个概念近两年非常热,正在慢慢改变 DevOps 的思路.微服务架构把一个庞大的业务系统拆解开来,每一个组件变得更加独立自治.松耦合.但是,同时也伴随着部署单元粒度越来越 ...

  5. 【学习】Zepto与jQuery 差别

    前几天遇到一个项目,需要把jquery全部改成Zepto,当时因为自己没有实际经验,所以没有接.今天查了一下两者究竟有什么区别. 首先看到了这么一篇文章:http://www.bootcss.com/ ...

  6. Set 和 Map 数据结构

    Set Set 对象允许你存储任何类型的 唯一值, 无论是 原始值(一共6种,string, number, boolean, undefined, null,和 es6 新增的 symbol) 还是 ...

  7. JavaScript责任链模式

    介绍 责任链模式(Chain of responsibility)是使多个对象都有机会处理请求,从而避免请求的发送者和接受者之间的耦合关系.将对象连成一条链,并沿着这条链传递该请求,直到有一个对象处理 ...

  8. Linux入门(7)——Ubuntu16.04安装wps并解决系统缺失字体问题

    进入WPS官网下载deb包: http://linux.wps.cn/ 安装,打开终端: cd 下载 ~a21_amd64.deb 解决打开WPS时出现的系统缺失字体问题: 下载 https://pa ...

  9. java选择结构

    /**选择结构*/ public class Demo06{ public static void main(String []args){ // if...else...型 String name ...

  10. bootstrap select多选

    1.页面效果 <div class="form-group"> <div class="col-md-2 control-label"> ...