Redux-Saga的理解与实现
2019-07-21 本文已影响0人
吴晗君
基础流程
在saga中需要对不同的业务写不同的generator函数,然后通过take(redux-saga暴露的方法)监听对应的action-type,内部是通过发布订阅存储在内存中。订阅的回调函数就是内部的一个next方法,这个next的作用是:通过generator生成的iterator中的next方法来持续迭代generator函数。
当组件中dispatch对应的action type的时候,redux-saga中间件就会publish(action type),来执行订阅函数。这个时候,就会执行对应generator的next方法。比如这时候就去请求数据,也是通过yield API.getSomething()的写法来拿到数据,然后在下一步通过put来发出修改redux中store数据的action。执行put的时候,会在next函数中直接dispatch出一个真实的action,来改变仓库中的数据。
知识点
主要点在于是在yield的用法,以及如何通过发布订阅与React组件、yield函数执行发生联系。以及对多种情况的公共处理:take、takeEvery、put、delay、fork、call、cps、cancel。
用法
import { put, all, call, take, takeEvery, fork, cancel } from "../../redux-saga/effects"
import * as types from '../action-types'
function delay (ms) {
return new Promise((resolve) => setTimeout(resolve, ms, ms))
}
// function* incrementAsync () {
// yield call(delay, 1000)
// yield put({type: types.INCREMENT})
// }
function* watchIncrementAsync() {
debugger
// for (let i=0;i<3;++i) {
// // yield take(types.ASYNC_INCREMENT)
// // yield call(delay, 1000)
// yield put({type: types.INCREMENT})
// }
yield put({type: types.INCREMENT})
console.log('所有generator函数已完成执行')
}
function* helloSaga (actionType) {
console.log('logger', actionType)
// const task =
// yield fork(task)
}
// 自执行每秒加1
function* increment () {
while(true){
console.log('22222')
yield delay(1000)
console.log('11111')
yield put({type:types.INCREMENT})
}
}
function* incrementWatcher () {
const task = yield fork(increment)
console.log('incrementWatcher')
yield take(types.CANCEL_INCREMENT)
console.log('take')
yield cancel(task)
}
export default function* rootSaga () {
console.log(666666)
// debugger
// yield watchIncrementAsync()
yield incrementWatcher()
// all([
// incrementWatcher()
// // takeEvery(types.ASYNC_INCREMENT, helloSaga)
// ])
// yield takeEvery(types.ASYNC_INCREMENT, helloSaga)
// yield takeEvery(types.ASYNC_INCREMENT, watchIncrementAsync)
console.log('所有generator函数全都执行完毕')
}
实现
effect.js
export function call (fn, ...args) {
return {
type: 'CALL',
fn,
args
}
}
export function put (action) {
return {
type: 'PUT',
action
}
}
export function take (actionType) {
return {
type: 'TAKE',
actionType
}
}
// fork的作用是不阻塞generetor函数,再开一个generator函数来做对应的事情。
export function fork (task) {
return {
type: 'FORK',
task
}
}
export function* takeEvery (actionType, task) {
yield fork(function* () {
while(true) {
yield take(actionType)
yield task(actionType)
}
})
}
const innerDelay= ms => new Promise((resolve) => setTimeout(resolve, ms, ms))
export function delay(...args) {
return call(innerDelay, ...args)
}
// 用以处理回调函数的形式
export function cps (fn, ...args) {
return {
type: 'CPS',
fn,
args
}
}
export function all (fns) {
return {
type: 'ALL',
fns
}
}
export function cancel (task) {
return {
type: 'CANCEL',
task
}
}
createSagaMiddleware.js
const isIterator = (it) => typeof it[Symbol.iterator] === 'function'
const isPromise = (o) => typeof o.then === 'function'
const times = (cb, total) => {
for (let i = 0; i < total; ++i) {
if(i === total){
cb()
}
}
}
export default function createSagaMiddleware () {
function createChannel () {
let _listeners = {}
function subscribe (actionType, cb) {
console.log(`订阅${actionType}`)
_listeners[actionType] = _listeners[actionType] || []
const once = () => {
cb()
off(actionType, once)
}
once._o = cb
_listeners[actionType].push(once)
}
function publish(actionType) {
const nexts = _listeners[actionType]
if (nexts && nexts.length) {
nexts.forEach(next => next())
}
}
function off (key, cb) {
if (_listeners[key]) {
_listeners[key] = _listeners[key].filter(l => l !== cb && l._o !== cb)
}
}
return {
subscribe,
publish
}
}
// 创建监听管道,其实就是发布订阅
let channel = createChannel()
const sagaMiddleware = ({dispatch, getState}) => {
function run (gen, cb) {
const it = isIterator(gen) ? gen : gen()
function next (returnValue) {
// effect是saga出得一个概念,指的是
// 一些简单 Javascript 对象,包含了要被 middleware 执行的指令。
// 当 middleware 拿到一个被 Saga yield 的 Effect,它会暂停 Saga,
// 直到 Effect 执行完成,然后 Saga 会再次被恢复。
// next函数参数是上一次yield 返回值。
const {value:effect, done} = it.next(returnValue)
if (!done) {
if (isIterator(effect)) {
run(effect)
next()
} else if (isPromise(effect)) { // 处理yield + promise的情况
effect.then(next)
} else {
const {type, actionType, action, task, fn, args, fns} =effect
switch (type) {
case 'TAKE':
// take会再yield的地方等待,
// 等组件dispatch的时候,调用next执行监听的内容(写在take()后面的代码)。
console.log('take----', actionType)
channel.subscribe(actionType, next)
break;
case 'PUT':
console.log('put----')
dispatch(action)
next() // put不阻塞
break;
case 'FORK':
console.log('fork----')
const newTask = task()
run(newTask)
next(newTask)
break;
case 'CALL': // call返回的都是promise
console.log('call----')
fn(...args).then(next)
break;
case 'CPS':
console.log('cps----')
fn(...args, next)
break;
case 'ALL': // 直到所有all完成(所有generator函数调用next函数皆返回{d}one:true})
console.log('all----')
const done = times(next, fns.length)
fns.forEach((fn) => run(fn, done))
break;
case 'CANCEL': // 直到所有all完成(所有generator函数调用next函数皆返回{d}one:true})
console.log('cancel----', task)
task.return('中断执行')
break;
}
}
} else {
cb && cb()
}
}
next()
}
// run放到sagaMiddleware函数里面的目的是需要拿到dispatch方法
sagaMiddleware.run = run
return (next) => (action) => {
const type = action.type
channel.publish(type)
next(action)
}
}
return sagaMiddleware
}