探究redux的反作用处理以及dispatch的promiseify

文章首发于语雀 探究redux的反作用处理以及dispatch的promiseify 源码分析javascript

未经容许严禁转载java

咱们知道redux的做用是管理应用程序的状态,让复杂应用可以更好得处理数据,使视图与数据一一对应。那redux的运行机制是什么样的呢?大体讲一下,就是用户行为产生action,dispatch接收action,而后经过reducer处理后生成新的state,最后更新store以及视图。看👇图react

image.png

反作用

redux的设计思想就是不产生反作用,数据更改的状态可回溯,因此redux中到处都是纯函数。git

那怎么处理反作用呢?redux没有提供解决直接的方案。可是它提供一个中间件机制,让用户去开发反作用处理的中间件。不少优秀的中间件也随着出现,如redux-thunk、redux-promise、redux-saga等。那它们是如何处理反作用的呢?请看👇github

redux-thunk

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

redux-promise

// 判断是否是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

用法和代码就不作探讨了。说说概念吧,redux-saga创造了一个saga层,专门用来处理反作用的。那么redux-saga是怎么处理反作用的呢? 首先,用户行为产生 action,派发 reducer 时,saga层监听到特定的 action(redux-saga提供一些辅助函数,用来监听将被派发到 reduceraction)进行处理。处理 action 的函数叫作 EffectEffect是个 Generate 函数,提供的 yield 能够用来控制代码执行,因此redux-saga适合处理异步。而且在 Effect 中也能继续发起一个普通 action,让 reducer 处理。 这就是redux-saga的大体执行过程。好了,如今进入本文第二个主题,如何使 dispatch promiseify异步

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 属性值。

好了,暂时就这些概念,接下来看看怎么实现吧。 用户发起 actionreducer 后。由于 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
})
复制代码

实现了 dispatchpromiseify,但不以为每次写个 Effect,还须要写 resolve,这样不麻烦吗?那咱们转变一下思路,能不能再有个 Effect 专门来包装实际的 Effect,外层 Effectresolve 呢?

// 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
})
复制代码

好了,dispatchpromiseify 就实现了,是否是挺简单的,这只是个粗略版本,还能够更灵活好用,须要深刻了解的,能够去dva的源码。

总结

redux是目前最流行的react的状态管理库。因为redux的设计思想,致使反作用是个问题,但也不难解决,有优秀的中间件能够去解决。在使用redux和redux-saga时,若是想使 dispatch promiseify 呢,能够去写个中间件,再搭配一个外层 Effect 就ok了。