redux-saga 是一个用于管理应用程序 Side Effect(副作用,例如异步获取数据,拜访浏览器缓存等)的 library,它的指标是让副作用治理更容易,执行更高效,测试更简略,在解决故障时更容易。

作为 redux 的中间件,redux-saga 提供了更加优雅的解决异步action的形式,redux-saga通过Generator函数来决定每次动作的暂停执行提早勾销等操作,
一个 saga 就像是应用程序中一个独自的线程,它单独负责解决副作用。

在redux中应用redux-saga

import { createStore, applyMiddleware } from 'redux'import createSagaMiddleware from 'redux-saga'import reducer from './reducers'import mySaga from './sagas'// create the saga middlewareconst sagaMiddleware = createSagaMiddleware()// mount it on the Storeconst store = createStore(reducer,applyMiddleware(sagaMiddleware))// then run the sagasagaMiddleware.run(mySaga)// render the application

Middleware API

createSagaMiddleware

createSagaMiddleware函数目标是创立sagaMiddleware中间件,同时咱们也晓得sagaMiddleware函数绑定了run办法

function createSagaMiddleware(){  return function sagaMiddleware({dispatch,getState}){    // 给sagaMiddleware绑定run函数    sagaMiddleware.run = function(generator){}    // 中间件函数    return (next) => (action) => {      next(action);    }  }}

middleware.run

sagaMiddleware.run = function (generator, callback) {  // 判断 generator 是否函数,返回执行后果或者自身  const iterator = typeof generator === "function" ? generator() : generator;  // next 函数判断下一次的动作  function next(action){    const { value: effect, done } = iterator.next();    //如果generator实现,则执行回调函数    if(done){      callback && callback();      return;    }    // 如果 effect 是一个 generator    if (typeof effect.next === "function"){      run(effect,next);    }    // 如果 effect 是一个 Promise,在promise完结后继续执行next    if (effect instanceof Promise){       effect.then(next)    }  }  next();}

Effect 创立器

redux-saga能够看成由一个个的 effect 所组成,effect 代表了每次执行的指令,新建 effects.js 文件用来创立 effect

take

take函数可能阻塞generator,只有在中间件action中触发能力往下执行,这里能够了解成公布订阅的一个操作,能够在外部创立一个channel函数用来创立公布订阅器:

function createChannel() {  let listener = {};  let subscribe = (actionType, callback) => listener[actionType] = callback;  let publish = (action) => {    if (!listener[action.type]) return;    let actionFn = listener[action.type];    delete listener[action.type];    actionFn(action);  }  return { subscribe, publish };}
const channel = createChannel();...return (next) => (action) => {  channel.publish(action); //中间件派发每次action动作  next(action);}...

effects文件中创立take函数,导出并申明type类型:

// effects.jsexport function take(actionType){  return {    type:"TAKE",    actionType  }}

next函数中,判断effect类型,而后订阅actionType,并将下一次的next操作传递给订阅函数,当中间件函数中捕捉到action时,能力触发到下一次的函数执行:

switch (effect.type) {  case "TAKE":    channel.subscribe(effect.actionType,next);    break; }

put

put办法执行action操作,间接在effect函数中返回action,next函数中截取到并dispatch

// effects.jsexport function put(action){  return {    type:"PUT",    action  }}
case "PUT":  dispatch(effect.action);  next(effect.action);  break;

fork

effects函数中创立fork

// effects.jsexport function fork(task) {  return {    type:"FORK",    task  }}

next中截取到FORK类型,task可能是个generator函数,所以传递给run办法执行,
并且保留forkTask,不便前面做勾销工作的操作:

case "FORK":  let forkTask = effect.task();  sagaMiddleware.run(forkTask);  next(forkTask);  break;

cancel

generator中能够通过return办法终止工作的执行,所以咱们能够拿到后面fork保留的generator,进行终止操作:

// effects.jsexport function cancel(task) {  return {    type:"CANCEL",    task  }}
case "CANCEL":  effect.task.return('over')  break;

takeEvery

takeEvery能够了解成对fork的封装,用while将每一次的工作监听起来:

//effects.jsexport function* takeEvery(actionType,task) {  yield fork(function* (){    while(true){      yield take(actionType);      yield task();    }  })}

call

call办法承受promise函数并执行:

//effects.jsexport function call(fn,...args) {  return {    type:"CALL",    fn,    args  }}

所以在next中,如果是call类型则在promise完结后才执行下一个next

case "CALL":  effect.fn(...effect.args).then(next)  break;

cps

call办法承受回调函数并执行:

//effects.jsexport function cps(fn,...args) {  return {    type:"CPS",    fn,    args  }}
case "CPS":  effect.fn(...effect.args,next);  break;

all

all办法须要等所有的generator函数执行完能力往下执行

//effects.jsexport function all(fns) {  return {    type:"ALL",    fns  }}
/** @params cb [function] 回调函数* @params total [number] 执行数量*/function times(cb, total) {  let index = 0;  return () => {    index++;    if (index >= total) {      cb && cb();    }  }}...case "ALL":  const total = effect.fns.length;  const over = times(next, total);  effect.fns.forEach(fn => run(fn, over))  break;...