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 middleware
const sagaMiddleware = createSagaMiddleware()
// mount it on the Store
const store = createStore(reducer,applyMiddleware(sagaMiddleware))
// then run the saga
sagaMiddleware.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.js
export 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.js
export function put(action){
return {
type:"PUT",
action
}
}
case "PUT":
dispatch(effect.action);
next(effect.action);
break;
fork
effects
函数中创建fork
:
// effects.js
export 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.js
export function cancel(task) {
return {
type:"CANCEL",
task
}
}
case "CANCEL":
effect.task.return('over')
break;
takeEvery
takeEvery
可以理解成对fork
的封装,用while
将每一次的任务监听起来:
//effects.js
export function* takeEvery(actionType,task) {
yield fork(function* (){
while(true){
yield take(actionType);
yield task();
}
})
}
call
call
方法接受promise函数并执行:
//effects.js
export 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.js
export function cps(fn,...args) {
return {
type:"CPS",
fn,
args
}
}
case "CPS":
effect.fn(...effect.args,next);
break;
all
all
方法需要等所有的generator
函数执行完才能往下执行
//effects.js
export 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;
...