思维导图
loginSaga(demo)
// import { call, put, takeEvery, take, fork } from "redux-saga/effects";
import { call, put, take, fork } from "../saga-nut/effects";
import LoginService from "../service/login";
import {
LOGIN_FAILURE,
LOGIN_SAGA,
LOGIN_SUCCESS,
REQUEST,
} from "../store/const";
function* loginHandle(action) {
yield put({ type: REQUEST });
try {
const res1 = yield call(LoginService.login, action.payload);
const res2 = yield call(LoginService.getMoreUserInfo, res1);
yield put({ type: LOGIN_SUCCESS, payload: res2 });
} catch (err) {
yield put({ type: LOGIN_FAILURE, payload: err });
}
}
// watcher saga
function* loginSaga() {
// yield takeEvery(LOGIN_SAGA, loginHandle);
while (true) {
const action = yield take(LOGIN_SAGA);
yield fork(loginHandle, action);
}
}
export default loginSaga;
store
import { createStore, combineReducers, applyMiddleware } from "redux";
// import thunk from "redux-thunk";
// import createSagaMiddleware from "redux-saga";
import createSagaMiddleware from "../saga-nut";
// !1. 创建要运行的saga
import loginSaga from "../action/loginSaga";
import rootSaga from "../action/rootSaga";
import { loginReducer } from "./loginReducer";
// !2. 创建saga中间件
const sagaMiddleware = createSagaMiddleware();
const store = createStore(
combineReducers({ user: loginReducer }),
// !3. 把saga中间件与redux store链接
applyMiddleware(sagaMiddleware)
);
// !4. 运行saga
sagaMiddleware.run(loginSaga, "xixi");
export default store;
入口
import { stdChannel } from "./channel";
import runSaga from "./runSaga";
export default function createSagaMiddleware() {
let boundRunSaga;
let channel = stdChannel();
// redux-thunk logger promise
function sagaMiddleware({ getState, dispatch }) {
boundRunSaga = runSaga.bind(null, { channel, getState, dispatch });
return (next) => (action) => {
debugger;
console.log("111");
let result = next(action);
channel.put(action);
return result;
};
}
sagaMiddleware.run = (...args) => boundRunSaga(...args);
return sagaMiddleware;
}
- 入口里主要是实现了一个中间件,与redux-thunk和redux-logger一样,createSagaMiddleware返回一个函数,且该返回的函数里需传入getState和dispatch
- runSaga.bind主要是为了让saga获取store的参数,从而实现两者之间的通信
- sagaMiddleware.run需用户手动执行相应的saga调用
runSaga
import proc from "./proc";
export default function runSaga(
{ channel, getState, dispatch },
saga,
...args
) {
// console.log(args, "000");
debugger;
const iterator = saga(...args);
proc({ channel, getState, dispatch }, iterator);
}
这里主要是调用执行saga函数,然后进入到iterator的执行过程
iterator的主要处理过程
import { IO } from "./symbol";
import effectRunnerMap from "./effectRunnerMap";
//处理iterator
export default function proc(env, iterator) {
next();
function next(arg, isErr) {
let result;
if (isErr) {
result = iterator.throw(arg);
} else {
result = iterator.next(arg);
}
// value done
if (!result.done) {
debugger;
//
digestEffect(result.value, next);
}
}
function digestEffect(effect, cb) {
let effectSettled;
function currentCb(res, isErr) {
if (effectSettled) {
return; //防止死循环,如果副作用已经处理了,直接返回
}
effectSettled = true;
cb(res, isErr);
}
runEffect(effect, currentCb);
}
function runEffect(effect, currentCb) {
if (effect && effect[IO]) {
const effectRunner = effectRunnerMap[effect.type];
effectRunner(env, effect.payload, currentCb);
} else {
//如果没有副作用,那就直接执行下一个iterator
currentCb();
}
}
}
这里特别需要注意genarator函数的特点,iterator.next()传入的参数是上一个yield的返回值,itarator.next()的返回值是一个{IO,type,payload}这样的对象。这里的实现是递归的起点
各种effects iterator的处理
import effectTypes from "./effectTypes";
import { promise } from "./is";
import proc from "./proc";
// todo channel
function runTakeEffect(env, { channel = env.channel, pattern }, cb) {
const matcher = (input) => input.type === pattern;
channel.take(cb, matcher);
}
function runPutEffect(env, { action }, cb) {
const result = env.dispatch(action);
cb(result);
}
function runCallEffect(env, { fn, args }, cb) {
const result = fn.apply(null, args);
if (promise(result)) {
result.then((data) => cb(data)).catch((err) => cb(err, true));
return;
}
cb(result);
}
function runForkEffect(env, { fn, args }, cb) {
const taskIterator = fn.apply(null, args);
proc(env, taskIterator);
cb();
}
function runALlEffect(env, { effects }, cb) {
let n = effects.length;
for (let i = 0; i < n; i++) {
proc(env, effects[i]);
}
}
const effectRunnerMap = {
[effectTypes.TAKE]: runTakeEffect,
[effectTypes.PUT]: runPutEffect,
[effectTypes.CALL]: runCallEffect,
[effectTypes.FORK]: runForkEffect,
[effectTypes.ALL]: runALlEffect,
};
export default effectRunnerMap;
- 这里的cb回调都是去处理下一个iterator
- proc的调用是因为执行了一个saga函数,组要重新进行一个iterator的处理过程
- channel.take是对iterator处理开始的一个注册
effects
import effectTypes from "./effectTypes";
import { IO } from "./symbol";
function makeEffect(type, payload) {
return {
[IO]: IO,
type,
payload,
};
}
export function take(pattern) {
return makeEffect(effectTypes.TAKE, { pattern });
}
export function put(action) {
return makeEffect(effectTypes.PUT, { action });
}
export function call(fn, ...args) {
return makeEffect(effectTypes.CALL, { fn, args });
}
export function fork(fn, ...args) {
return makeEffect(effectTypes.FORK, { fn, args });
}
export function all(effects) {
return makeEffect(effectTypes.ALL, { effects });
}
这里每个effect返回的都是{IO,type,payload}的形式,与proc里iterator的处理相对应
export default {
TAKE: "TAKE",
PUT: "PUT",
ALL: "ALL",
RACE: "RACE",
CALL: "CALL",
CPS: "CPS",
FORK: "FORK",
JOIN: "JOIN",
CANCEL: "CANCEL",
SELECT: "SELECT",
ACTION_CHANNEL: "ACTION_CHANNEL",
CANCELLED: "CANCELLED",
FLUSH: "FLUSH",
GET_CONTEXT: "GET_CONTEXT",
SET_CONTEXT: "SET_CONTEXT",
};
const createSymbol = (name) => `@@redux-saga/${name}`;
export const CANCEL = createSymbol("CANCEL_PROMISE");
export const CHANNEL_END_TYPE = createSymbol("CHANNEL_END");
export const IO = createSymbol("IO");
export const MATCH = createSymbol("MATCH");
export const MULTICAST = createSymbol("MULTICAST");
export const SAGA_ACTION = createSymbol("SAGA_ACTION");
export const SELF_CANCELLATION = createSymbol("SELF_CANCELLATION");
export const TASK = createSymbol("TASK");
export const TASK_CANCEL = createSymbol("TASK_CANCEL");
export const TERMINATE = createSymbol("TERMINATE");
export const SAGA_LOCATION = createSymbol("LOCATION");
export const func = (f) => typeof f === "function";
export const promise = (p) => p && func(p.then);
channel
import { MATCH } from "./symbol";
export function stdChannel() {
const currentTakers = [];
function take(cb, matcher) {
cb[MATCH] = matcher;
currentTakers.push(cb);
}
// action
function put(input) {
const takers = currentTakers;
for (let i = 0, len = takers.length; i < len; i++) {
const taker = takers[i];
if (taker[MATCH](input)) {
taker(input);
}
}
}
return { take, put };
}
- take主要是注册iterator处理过程的入口
- 当用户执行dispatch时,put则是将take里注册的iterator拿出来进行下一步执行
总结
saga的核心原理就是帮用户自动执行generator函数,沿着这个思路其实就能把saga源码的实现串联起来