首页 男生 其他 深入浅出React和Redux

9.1.3 Promise中间件

深入浅出React和Redux 程墨 6039 2021-04-06 02:29

  您可以在百度里搜索“深入浅出React和Redux 艾草文学(www.321553.xyz)”查找最新章节!

  

  9.1.3 Promise中间件

  理解中间件的最好办法就是自己开发一个中间件,在上一章中提到过,替换redux-thunk中间件来实现异步action对象还有一个办法是利用Promise,Promise更加适合于输入输出操作,而且fetch函数返回的结果就是一个Promise对象,接下来我们就实现一个用于处理Promise类型action对象的中间件。

  一个最简单的Promise中间件实现方式是这样:

  function isPromise(obj) {

  return obj && typeof obj.then === 'function';

  }

  export default function promiseMiddleware({dispatch}) {

  return function(next) {

  return function(action) {

  return isPromise(action) ? action.then(dispatch) : next(action);

  }

  }

  }

  逻辑很简单,类似于redux-thunk判断action对象是否是函数,在这里判断action对象是不是一个Promise。如果不是,那就放行通过next让其他中间件继续处理。如果是,那就让这个Promise类型的action对象在完成时调用dispatch函数把完成的结果派发出去。

  这个实现很简单,但并不十分实用。

  我们在第7章的Weather应用中说到过,做一个完备的异步操作,要考虑三个状态,分别是“异步操作进行中”、“异步操作成功完成”和“异步操作失败”。上面的Promise中间件只考虑了第二种状态“异步操作成功完成”,这是不完整的,使用这样的Promise中间件,并不比redux-thunk省事。

  我们需要重新思考一下如何使用Promise。

  对于redux-thunk,实现异步就是要在中间件层执行一个指定子程序,而redux-thunk就是用action对象是不是函数类型来判断是否调用子程序。严格来说,我们完全可以写一个中间件,通过判断action对象上的async或者什么其他字段,代码如下:

  const thunk = ({ dispatch, getState }) => next => action => {

  if (typeof action.async === 'function') {

  return action.async(dispatch, getState);

  }

  return next(action);

  };

  然后只要约定异步action对象依然是普通JavaScript对象,只是多一个名为async的函数类型字段。

  如果能够这样理解action对象,那么我们也没有必要要求Promise中间件处理的异步action对象是Promise对象了,只需要action对象某个字段是Promise字段就行,而action对象可以拥有其他字段来包含更多信息。

  改进版本的Promise中间件是这样:

  export default function promiseMiddleware({dispatch}) {

  return (next) => (action) => {

  const {types, promise, ...rest} = action;

  if (!isPromise(promise) || !(action.types && action.types.length === 3)) {

  return next(action);

  }

  const [PENDING, DONE, FAIL] = types;

  dispatch({...rest, type: PENDING});

  return action.promise.then(

  (result) => dispatch({...rest, result, type: DONE}),

  (error) => dispatch({...rest, error, type: FAIL})

  );

  };

  }

  这个中间件处理的异步对象必须包含一个promise字段和一个types字段,前者当然是一个Promise对象,后者则必须是一个大小为3的数组,依次分别代表异步操作的进行中、成功结束和失败三种action类型。具体是什么值,应该在对应功能组件的action-Types.js文件中定义:

  一个被Promise中间件处理的异步action对象的例子是这样:

  {

  promise: fetch(apiUrl),

  types: ['pending', 'success', 'failure']

  }

  如果传入的action对象不满足这种格式,就直接通过next交给其他中间件处理。

  如果确定传入的action对象满足条件,那么Promise中间件就从action对象的types字段提取出三个action类型,第一个是表示异步操作进行中的PENDING,不用多说,先制造一个type为PENDING的action对象派发出去,告诉系统这个异步动作已经开始了。

  接下来,通过then和catch分别连接上promise字段,当这个字段代表的Proimse对象成功完成时,派发一个type为DONE的action对象,失败的时候派发一个type为FAIL的action对象。

  在这里PENDING、DONE和FAIL都是变量,是最初异步对象中types字段提取出来的,由异步动作的发起者决定,所以这一个Promise中间件能够支持任意种类的异步请求。

  这里也有一个约定,所有表示异步操作成功的action对象由result字段记录成功结果,表示异步操作失败的action对象用error字段记录失败原因。

  有了上面的Promise中间件,可以大大简化使用异步动作的模块代码。

  在第7章中的Weather应用,如果我们用这个Promise中间件取代redux-thunk,那么weather组件的actions.js文件可以改写,对应fetchWeather的动作构造函数代码如下:

  export const fetchWeather = (cityCode) => {

  const apiUrl = `/data/cityinfo/${cityCode}.html`;

  return {

  promise: fetch(apiUrl).then(response => {

  if (response.status !== 200) {

  throw new Error('Fail to get response with status ' + response.status);

  }

  return response.json().then(responseJson => responseJson.weatherinfo);

  }),

  types: [FETCH_STARTED, FETCH_SUCCESS, FETCH_FAILURE]

  };

  }

  原来的三个action构造函数fetchWeatherStarted、fetchWeatherSuccess和fetchWeather-Failure都不见了,因为他们的逻辑都被抽象出来放到Promise中间件中去了,在这个文件中只需要通过异步action对象的types字段把三个定义好的action类型设上就可以。

  对比redux-thunk中间件和这个Promise中间件可以发现区别,如果应用redux-thunk,实际发起异步操作的语句是在中间件中调用的;而如果应用Promise中间件,异步操作是在中间件之外引发的,因为只有异步操作发生了才会有Promise对象,而Promise中间件只是处理这个对象而已。

  读者可能会发现,Promise中间件要求“异步操作成功”和“异步操作失败”两个action对象必须用result和error字段记录结果,这样缺乏灵活性,的确是的。有一个改进方法是不用types传入action类型,而是用例如actionCreators名字的字段传入三个action构造函数,这样就能够更灵活地产生action对象,代价就是每个模块需要定义那三个action构造函数,读者可以尝试实现这种中间件。 深入浅出React和Redux

目录
设置
手机
书架
书页
评论