Redux-Saga 初识和总结

做者介绍:罗雪婧,美团点评前端工程师,3年 Web 前端开发经验,如今是美团点评点餐团队的一员。javascript

1、Redux-Saga介绍

redux-saga 是一个旨在于在React/Redux应用中更好、更易地解决异步操做(action)的库。主要模块是 saga 会像一个分散的支线在你的应用中单独负责解决异步的action(相似于后台运行的进程)。详细移步:Redux-saga前端

redux-saga至关于在Redux原有数据流中多了一层,对Action进行监听,捕获到监听的Action后能够派生一个新的任务对state进行维护(固然也不是必需要改变State,能够根据项目的需求设计),经过更改的state驱动View的变动。图以下所示:java

用过redux-thunk的人会发现,redux-saga 其实和redux-thunk作的事情相似,都是能够处理异步操做和协调复杂的dispatch。不一样点在于:git

  • Sagas 是经过 Generator 函数来建立的,意味着能够用同步的方式写异步的代码;
  • Thunks 是在 action 被建立时才调用,Sagas 在应用启动时就开始调用,监听action 并作相应处理; (经过建立 Sagas 将全部的异步操做逻辑收集在一个地方集中处理)
  • 启动的任务能够在任什么时候候经过手动取消,也能够把任务和其余的 Effects 放到 race 方法里能够自动取消;

2、入门demo

redux-saga-beginner-tutorialgithub

$ git clone https://github.com/HelianXJ/redux-saga-beginner-tutorial.git
$ git checkout redux-tool-saga // 切到有redux tool的分支配合chorme 的 Redux DevTools 工具查看逻辑更清晰

$ npm i  //下载依赖
$ npm run hello //先看项目文件中的hello sagas复制代码

启动server成功后view-on: http://172.22.32.14:9966/ npm

可看到以下界面,一个简单的例子,点击say hello按钮展现 hello,点击say goodbye按钮展现goodbye。可注意看右边栏的Action变化和console控制台的输出。redux


sagas.js 关键代码前端工程师

import { takeEvery } from 'redux-saga';

export function* helloSaga() {
  console.log('Hello Sagas!');
}

export default function* watchIncrementAsync() {
    yield* takeEvery('SAY_HELLO', helloSaga);
}复制代码

这里sagas建立了一个watchIncrementAsync 监听SAY_HELLO的Action,派生一个新的任务——在控制台打印出“Hello Sagas!”经过这例子能够理解redux-saga大体作的事情。并发

该项目中还有一个计数器的简单例子。异步

$ npm start //便可查看Counter的例子复制代码

sagas.js关键代码

// 一个工具函数:返回一个 Promise,这个 Promise 将在 1 秒后 resolve
export const delay = ms => new Promise(resolve => setTimeout(resolve, ms))

// Our worker Saga: 将异步执行 increment 任务
export function* incrementAsync() {
    yield delay(1000);
      yield put({ type: 'INCREMENT' });
}

// Our watcher Saga: 在每一个 INCREMENT_ASYNC action 调用后,派生一个新的 incrementAsync 任务
export default function* watchIncrementAsync() {
      yield* takeEvery('INCREMENT_ASYNC', incrementAsync);
}复制代码

计数器例子的单元测试 sagas.spec.js 关键代码

import test from 'tape';
import { put, call } from 'redux-saga/effects'
import { incrementAsync, delay } from './sagas'

test('incrementAsync Saga test', (assert) => {
  const gen = incrementAsync()

  assert.deepEqual(
    gen.next().value,
    call(delay, 1000),
    'incrementAsync Saga must call delay(1000)'
  )

  assert.deepEqual(
    gen.next().value,
    put({type: 'INCREMENT'}),
    'incrementAsync Saga must dispatch an INCREMENT action'
  )

  assert.deepEqual(
    gen.next(),
    { done: true, value: undefined },
    'incrementAsync Saga must be done'
  )

  assert.end()
});复制代码

因为redux-saga是用ES6的Generators实现异步,incrementAsync 是一个 Generator 函数,因此当咱们在 middleware 以外运行它,会返回一个易预见的遍历器对象, 这一点应用在单元测试中更容易写unit。

redux-saga能作的不仅是能够作以上例子的事情。

实际上 redux-saga 全部的任务都通用 yield Effects 来完成。它为各项任务提供了各类 Effect 建立器,能够是:

  • 调用一个异步函数;
  • 发起一个 action 到 Store;
  • 启动一个后台任务或者等待一个知足某些条件的将来的 action。

3、redux-sagas的使用

  • 组合sagas (yield Sagas) —— 实际上和redux-thunk 的dispatch 一个action相似
function* fetchPosts() {
  yield put( actions.requestPosts() )
  const products = yield call(fetchApi, '/products')
  yield put( actions.receivePosts(products) )
}

function* watchFetch() {
  while ( yield take(FETCH_POSTS) ) {
    yield call(fetchPosts) // waits for the fetchPosts task to terminate
  }
}复制代码

当 yield 一个 call 至 Generator,Saga 将等待 Generator 处理结束, 而后以返回的值恢复执行

  • 任务取消 —— 一旦任务被 fork,可使用 yield cancel(task) 来停止任务执行。取消正在运行的任务,将抛出 SagaCancellationException 错误。

  • 同时执行多个任务
const [users, repos] = yield [
     call(fetch, '/users'),
     call(fetch, '/repos')
 ]复制代码
  • 使用辅助函数管理 Effects 之间的并发。
function* takeEvery(pattern, saga, ...args) {
  while(true) const action = yield take(pattern)
    yield fork(saga, ...args.concat(action))
  }
}复制代码

3、Redux-Saga优势

  • 流程拆分更细,异步的action 以及特殊要求的action(更复杂的action)都在sagas中作统一处理,流程逻辑更清晰,模块更干净;
  • 以用同步的方式写异步代码,能够作一些async 函数作不到的事情 (无阻塞并发、取消请求)
  • 能容易地测试 Generator 里全部的业务逻辑
  • 能够经过监听Action 来进行前端的打点日志记录,减小侵入式打点对代码的侵入程度

4、带来的问题和可接受性

  • action 任务拆分更细,原有流程上至关于多了一个环节。对开发者的设计和抽象拆分能力更有要求,代码复杂性也有所增长。
  • 异步请求相关的问题较难调试排查
相关文章
相关标签/搜索