index.js:
import { channel } from './channel' //promise判断 const isPromise = p => { return typeof p.then == 'function' && typeof p.catch == 'function' } function createSagaMiddelware() { function sagaMiddelware({getState,dispatch}){ //负责把gernerator执行完毕 function run(iterator){ //执行得到迭代器,next得到值,可能是器也可能是迭代器 let it = typeof iterator == 'function'?iterator():iterator; function next(input){ let {value: effect,done}=it.next(input); //如果迭代器没有完成 if(!done){ //genrator if(typeof effect[Symbol.iterator] == 'function'){ run(effect); next(); } //延迟函数 if(isPromise(effect)) { effect .then(next) .catch(error => next(error)) } switch(effect.type){ //注册事件 case 'take': let {actionType}=effect; channel.subscribe(actionType,next); break; //走到put的事件就直接dispatch执行了 case 'put': let {action}=effect; dispatch(action); next(action); break; //fork继续执行 case 'fork': let {worker}=effect; run(worker); next(); break; //异步执行成功后再next case 'call': let {fn,args}=effect; fn(...args).then(next); break; default: break; } } } next() } sagaMiddelware.run = run; //中间件执行 return (next) => (action) => { next(action) channel.publish(action) } } return sagaMiddelware; } export default createSagaMiddelware;channel.js
function createChannel() { //对象每一个动作对应一个回调函数 let takers={}; function subscribe(actionType,cb) { takers[actionType]=cb; } function publish(action) { //监听 let taker=takers[action.type] if (taker) {//如果有执行监听函数并且删除监听函数 let tmp=taker; taker = null; tmp(action); } } return {subscribe,publish}; } export let channel = createChannel();effect.js
function take(actionType) { return { type: 'take', actionType } } function put(action) { return { type: 'put', action } } function fork(worker) { return { type: 'fork', worker } } function call(fn,...args) { return { type: 'call', fn, args } } //监听每一个动作类型,当此动作发生的时候执行对应的worker //takeEvery它会单开一个任务,并不会阻塞当前saga function* takeEvery(actionType,worker) { yield fork(function* () { while (true) { let action = yield take(actionType); yield worker(action); } }) } function delay(ms, val = true) { let timeoutId const promise = new Promise(resolve => { timeoutId = setTimeout(() => resolve(val), ms) }) promise['CANCEL_PROMISE'] = () => clearTimeout(timeoutId) return promise } //takerEvery的结果是一个迭代器 //iterator export { take, put, takeEvery, call, delay }使用: app.js:
import React from 'react'; import ReactDOM from 'react-dom'; import Root from './router'; import {createStore, applyMiddleware } from 'redux'; import { Provider } from 'react-redux'; import createSagaMiddleware from './testSaga/index.js' // import createSagaMiddleware from './saga/index' import { watchIncrementAsync, watchAndLog } from './sagaActions/index' //globe css import './style/index.styl'; import './style/less.less'; import './style/sass.sass'; import './style/scss.scss'; const initialState = { number: 0, list: [] }; const incrementReducer = (state = initialState, action) => { switch(action.type) { case 'INCREMENT': { state.number += 1 return { ...state } break } case 'DECREMENT': { return { ...state, list: action.data.data } break }; default: return state; } }; const sagaMiddleware = createSagaMiddleware(); const store = createStore(incrementReducer,applyMiddleware(sagaMiddleware)) sagaMiddleware.run(watchIncrementAsync) // sagaMiddleware.run(watchAndLog) ReactDOM.render( <Provider store={store}> <Root /> </Provider>, document.getElementById('app') );sagaAction:
// import { delay } from '../saga' // import { select, call, fork, take, put, takeEvery } from '../saga/effects' import { call, put, takeEvery, take, delay } from '../testSaga/effect' import {GetUserData} from '../fetch/api.js' export function* watchAndLog() { while (true) { const action = yield take('*') const state = yield select() console.log('action', action) console.log('state after', state) } } export function* incrementAsync() { yield delay(1000) yield put({ type: 'INCREMENT' }) } export function* indecrementAsyncs({ payload }) { //发起请求 payload是给请求函数的参数 const data = yield call(GetUserData, payload); yield put({ type: 'DECREMENT', data }) } //发起请求 function* fetchUrl(param) { const data = yield call(GetUserData, param); // 指示中间件调用 fetch 异步任务 yield put({ type: 'DECREMENT', data }); // 指示中间件发起一个 action 到 Store } export function* watchIncrementAsync() { yield takeEvery('INCREMENT_ASYNC', incrementAsync) yield takeEvery('DECREMENT_ASYNC', indecrementAsyncs) //或者 // while(true) { // const action = yield take('FETCH_REQUEST'); // 指示中间件等待 Store 上指定的 action,即监听 action // yield fork(fetchUrl, action.payload); // 指示中间件以无阻塞调用方式执行 fetchUrl // } }发起事件:
onClick() { this.props.dispatch({ type: 'DECREMENT_ASYNC', // type: 'FETCH_REQUEST', //参数的传递 payload: { name: 'test', s:11 } }) }执行流程分析: 初始化阶段: 执行createSagaMiddelware 返回 sagaMiddelware 执行 saga run方法 传入的是自定义的 watchIncrementAsync函数:这里面是takeEvery命令 拿到 iterator (watchIncrementAsync)并执行一下 初次执行一下 next 函数,此时next中 input参数为undefined 执行generator 的 next 获取 watchIncrementAsync中 第一个yield(takeEvery类型)的值 如果里面是iterator则继续run next 最终走到 fork ,fork里面因为是generator函数 则继续执行 run next 走到 type take 往channel监听器中添加 actionType 和 netx函数回调 这样dispatch 就可以执行这个next回调 初始化阶段完毕
点击dispatch执行阶段: dispatch takeEvery中的类型 找到初始化阶段添加注册的此类型并执行 netx 回调 然后it.next(input) 触发的是 yield worker(action); worker就是incrementAsync函数因为他是generator函数 所以会继续执行run()后 再next就可以继续yield 如果其中有 自定义 delay 延迟函数,则等待then结束再回调 next worker此时是逻辑执行函数如果是 yield put 就直接执行 dispatch(action); 并且next一下,后面可能有多个yield yield call 执行fn(…args).then(next);
