文章首发于语雀 探究redux的反作用处理以及dispatch的promiseify 源码分析javascript
未经容许严禁转载java
咱们知道redux的做用是管理应用程序的状态,让复杂应用可以更好得处理数据,使视图与数据一一对应。那redux的运行机制是什么样的呢?大体讲一下,就是用户行为产生action,dispatch接收action,而后经过reducer处理后生成新的state,最后更新store以及视图。看👇图react
redux的设计思想就是不产生反作用,数据更改的状态可回溯,因此redux中到处都是纯函数。git
那怎么处理反作用呢?redux没有提供解决直接的方案。可是它提供一个中间件机制,让用户去开发反作用处理的中间件。不少优秀的中间件也随着出现,如redux-thunk、redux-promise、redux-saga等。那它们是如何处理反作用的呢?请看👇github
function createThunkMiddleware(extraArgument) {
return ({ dispatch, getState }) => next => action => {
if (typeof action === 'function') {
return action(dispatch, getState, extraArgument);
}
return next(action);
};
}
const thunk = createThunkMiddleware();
thunk.withExtraArgument = createThunkMiddleware;
export default thunk;
复制代码
redux-thunk最重要的思想,就是能够接受一个返回函数的 action creator
。若是这个 action creator
返回的是一个函数,就执行它,若是不是,就按照原来的 next(action)
执行。 正由于这个 action creator
能够返回一个函数,那么就能够在这个函数中执行一些异步的操做。redux
// 判断是否是Promise函数
import isPromise from 'is-promise';
// 标准flux action
import { isFSA } from 'flux-standard-action';
export default function promiseMiddleware({ dispatch }) {
return next => action => {
// 首先判断action是否是flux规定的类型
if (!isFSA(action)) {
return isPromise(action) ? action.then(dispatch) : next(action);
}
return isPromise(action.payload)
? action.payload
.then(result => dispatch({ ...action, payload: result }))
.catch(error => {
dispatch({ ...action, payload: error, error: true });
return Promise.reject(error);
})
: next(action);
};
}
复制代码
redux-promise将 promise
贯彻到底。将 promise
做为 action
传给 dispatch
,让中间件处理 resolve
,能够去少写 .then().catch()
之类的代码。 redux-thunk和redux-promise用法实际比较相似,都是触发一个 function/promise
让中间件本身决定处理反作用的时机。这能解决大部分的反作用场景,但对于更复杂的反作用状况,就须要写大量代码。而redux-saga正是能很好得解决这个问题。promise
用法和代码就不作探讨了。说说概念吧,redux-saga创造了一个saga层,专门用来处理反作用的。那么redux-saga是怎么处理反作用的呢? 首先,用户行为产生 action
,派发 reducer
时,saga层监听到特定的 action
(redux-saga提供一些辅助函数,用来监听将被派发到 reducer
的 action
)进行处理。处理 action
的函数叫作 Effect
,Effect
是个 Generate
函数,提供的 yield
能够用来控制代码执行,因此redux-saga适合处理异步。而且在 Effect
中也能继续发起一个普通 action
,让 reducer
处理。 这就是redux-saga的大体执行过程。好了,如今进入本文第二个主题,如何使 dispatch promiseify
。异步
这里的 dispatch promiseify 指的是在使用redux和redux-saga状况下去实现的。那么开始吧。async
function* asyncIncrease(action) {
return action.payload
}
// 省略一些步骤
......
store.dispatch({
type: 'asyncIncrease', payload: 30
}).then(res => {
console.log(res); // 30
})
复制代码
由于 Effect
是个 Generate
函数,那先来瞧瞧 Generate
的一些概念吧。函数
Generator
函数是一个普通函数,可是有两个特征。一是,function
关键字与函数名之间有一个星号;二是,函数体内部使用 yield
表达式,定义不一样的内部状态Generator
函数后,该函数并不执行,返回的也不是函数运行结果,而是一个指向内部状态的指针对象yield
表达式,就暂停执行后面的操做,并将紧跟在 yield
后面的那个表达式的值,做为返回的对象的 value
属性值。好了,暂时就这些概念,接下来看看怎么实现吧。 用户发起 action
到 reducer
后。由于 reducer
是个纯函数,即相同的输入,永远会获得相同的输出,并且没有任何可观察的反作用。因此咱们得区分 action
,是由redux-saga处理仍是reducer处理。因此得有个中间件,而且返回一个 promise
,使 dispatch
可以使用then
方法。
const promiseMiddlware = () => next => action => {
// 经过type来判断是否是由Effect处理
// 若是是,返回promise
if (isEffect(action.type) {
return new Promise((resolve, reject) => {
next({
resolve,
reject,
...action,
})
})
} else {
return next(action)
}
}
复制代码
// action其实就包含了resolve,reject和payload
function* asyncIncreate(action) {
const { resolve, payload } = action
resolve(payload)
}
store.dispatch({
type: 'asyncIncrease', payload: 30
}).then(res => {
console.log(res); // 30
})
复制代码
实现了 dispatch
的 promiseify
,但不以为每次写个 Effect
,还须要写 resolve
,这样不麻烦吗?那咱们转变一下思路,能不能再有个 Effect
专门来包装实际的 Effect
,外层 Effect
去 resolve
呢?
// 1. 先写个外层的Effect
function* baseEffect(action) {
const {resolve, reject,...rest} = action
// 2. 经过yield执行实际的Effect
const res = yield asyncIncreate(rest)
resolve(res)
}
// asyncIncreate就返回值就ok了
function* asyncIncreate(action) {
return action.payload
}
// 省略createStore等步骤
store.dispatch({
type: 'asyncIncrease', payload: 30
}).then(res => {
console.log(res); // 30
})
复制代码
好了,dispatch
的 promiseify
就实现了,是否是挺简单的,这只是个粗略版本,还能够更灵活好用,须要深刻了解的,能够去dva的源码。
redux是目前最流行的react的状态管理库。因为redux的设计思想,致使反作用是个问题,但也不难解决,有优秀的中间件能够去解决。在使用redux和redux-saga时,若是想使 dispatch promiseify
呢,能够去写个中间件,再搭配一个外层 Effect
就ok了。