曾经前端的革新是以Ajax的出现为分水岭,现代应用中绝大部分页面渲染会以异步流的方式进行。在Redux中,如果要发起异步请求,最合适的位置是在action creator中实现。但我们之前了解到的action都是同步情况,因此需要引入中间件让action支持异步情况,如异步action(异步请求)为一个函数,或者利用promise来完成,或者是其他自定义的形式等等,下面的middleware就是用来处理这些不同异步action(或者说成actionCreator)的.另外,在Redux社区里还有其他一些处理异步的中间件,它们大同小异,这里就不一一分析了。

redux-thunk

redux-thunk 是 redux 官方文档中用到的异步组件,实质就是一个 redux 中间件,thunk 简单来说 就是一个封装表达式的函数,封装的目的是延迟执行表达式。

redux-thunk 是一个通用的解决方案,其核心思想是让 action 可以变为一个 thunk ,这样的话:

  1. 同步情况:dispatch(action)
  2. 异步情况:dispatch(thunk)

    thunk 本质上就是一个函数,函数的参数为 dispatch, 所以一个简单的 thunk 异步代码就是如下:

    this.dispatch(function (dispatch){setTimeout(() => {dispatch({type: 'THUNK_ACTION'}) }, 1000)
    })

之前已经讲过,这样的设计会导致异步逻辑放在了组件中,解决办法为抽象出一个 asyncActionCreator, 这里也一样,我们就叫 thunkActionCreator 吧,上面的例子可以改为:

export function createThunkAction(payload) {return function(dispatch) {setTimeout(() => {dispatch({type: 'THUNK_ACTION', payload: payload}) }, 1000)}
}
// someComponent.js
this.dispatch(createThunkAction(payload))

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;

思路:当action为函数的时候,我们并没有调用next或dispatch方法,而是返回action的调用。这里的action即为一个Thunk函数,以达到将dispatch和getState参数传递到函数内的作用。

此时,action可以写成thunk形式(ThunkActionCreator):

function getweather(url,params){return (dispatch,getState)=>{fetch(url,params).then(result=>{dispatch({type:'GET_WEATHER_SUCCESS',payload:result,});}).catch(err=>{dispatch({type:'GET_WEATHER_ERROR',error:err,});});
};
}

redux-promise

其实 thunk 我们已经有了处理异步的能力, 但是每次我们要自己去手动触发三个 action, 工作量还是很大的。现在 ajax 很多都会包装为 promise 对象,,异步请求其实都是利用promise来完成的 因此我们可以对与 dispatch 增加一层判断, 使得它具有处理具有 promise 属性的 action 的能力。

import {isFSA} from 'flux-standard-action';function isPromise(val){return next=>action=>{if(!isFSA(action)){return isPromise(action)? action.then(dispatch):next(action);
}return isPromise(action.payload)? action.payload.then(result=>dispatch({...action,payload:result}),error=>{dispatch({...action,payload:error,error:true});return Promise.reject(error);}): next(action);};
}

思路:redux-promise兼容了FSA标准(了解FSA可参考https://segmentfault.com/a/11...),也就是说将返回的结果保存在payload中。实现过程非常容易理解,即判断action或action.payload是否为promise,如果是,就执行then,返回的结果再发送一次dispatch。

此时,action可以写成promise形式(promiseActionCreator):

//利用ES7的async和awaita语法
const fetchData=(url,params)=>fetch(url,params);async function getWeather(url,params){const result=await fetchData(url,params);if(result.error){return{type:'GET_WEATHER_ERROR',error:'result.error',};}return{type:'GET_WEATHER_SUCCESS',payload:'result'};
}

redux-saga

redux-saga是redux社区一个处理异步流的后起之秀,它与上述方法最直观的不同就是用generator代替了promise。的确,redux-saga是最优雅的通用解决方案,它有着灵活而强大的协程机制,可以解决任何复杂的异步交互,具体的,放在另一篇文章中详细介绍。

为action定制的自定义异步中间件

在理想情况下,我们不希望通过复杂的方法去请求数据,而是希望通过如下形式一并完成在异步请求过程中的不同状态:

{url:'/api/weather.json',params:{city:encodeURL(city),}type:['GET_WEATHER','GET_WEATHER_SUCCESS','GET_WEATHER_ERROR'],
}

可以看到,异步请求action的格式有别于FSA。它并没有使用type属性,而使用了types属性。在请求middleware中,会对action进行格式检查,若存在url和types属性,则说明这个action是一个用于发送异步请求的action。此外,并不是所有请求都能携带参数,因此params是可选的。

const fetchMiddleware=store=>next=>action=>{if(!action.url || !Array.isArray(action.types)){return next(action);}const [LOADING,SUCCESS,ERROR]=action.types;next({type: LOADING,loading: true,...action,
});fetch(action.url,{params:action.params})
.then(result=>{next({type:SUCCES,loading:false,payload:result,});}).catch(err=>{next({type:ERROR,laoding:false,error:err,});});
}

使用middleware处理复杂异步流

在实际场景中,我们不但有短连接请求,还有轮询请求、多异步串联请求,或是在异步中加入同步处理的逻辑。这时我们需要对一般异步中间件进行处理。

轮询

轮询是长连接的一种实现方式,它能够在一定时间内重新启动自身,然后再次发起请求。基于这个特性,我们可以在上一个中间件的基础上再写一个middleware,这里命名为redux-polling:

import setRafTimeout,{clearRafTimeout} from 'setRafTimeout';export default ({dispatch,getState})=>next=>action{const {poolingUrl,params,types}=action;const isPollingAction=pollingUrl&&params&&types;if(!isPollingAction){return next(action);
}let timeoutId=null;
const startPolling=(timeout=0)=>{timeoutId=setRafTimeout(()=>{const pollingAction={...others,url:pollingUrl,timeoutId,};dispatch(pollingAction).then(data=>{if(data && data.interval && typeof data.interval=='number'){startPolling(data.interval*1000);}
else{console.error('pollingAction should fetch data contain interval');}
});},timeout);};startPolling();}export const clearPollingTimeout=(timeId)=>{if(timeoutId){clearRafTimeout(timeId);}
};

我们用到了raf函数,它可以让请求在一定时间内重新启动;startPolling函数为递归函数,这样可以,满足轮询的请求;在API的设计上,还暴露了clearPollingTimeout方法,以便我们在需要时手动停止轮询。

最后,调用action来发起轮询:

{
pollingurl:'/api/weather.json',
params:{city:encodeURL(city),},
types:[null,'GET_WEATHER-SUCCESS',null],
}

对于长连接,还有其他多种实现方式,最好的方式是对其整体做一次封装,在内部实现诸如轮询和WebSocket。

多异步串联

我们可以通过promise封装来实现不论是否是异步请求,都可以通过promise来传递以达到一个统一的效果。

const sequenceMiddlware=({dispatch,getState})=>next=>action=>{if(!Array.isArray(action)){return next(action);}return action.reduce((result,currAction)=>{return result.then(()=>{return Array.isArray(currAction)?Promise.all(currAction.map(item=>dispatch(item))):dispatch(currAction);});},Promise.resolve());
}

在构建action creator时,会传递一个数组,数组中每一个值都是按顺序执行的步骤。这里的步骤既可以是异步的,也可以是同步的。在实现过程中,我们非常巧妙地使用了Promise.resolve()来初始化action.reduce方法,然后使用Promise.then()方法串联起数组,达到了串联步骤的目的。

function getCurrCity(ip){return {url:'/api/getCurrCity.json',param: {ip},types: [null,'GET_CITY_SUCCESS',null],}
}return getWeather(cityId){return{url:'/api/getWeatherInfo.json',param:{cityId},types:[null,'GET_WEATHER_SUUCCESS',null],}
}function loadInitData(ip){return[getCurrCity(ip),(dispatch,state)=>{dispatch(getWeather(getCityIdWithState(state)));},];
}

这种方法利用了数组的特性,它已经覆盖了大部分场景,当然,如果串联过程中有不同的分支,就无能为力了。

Redux异步中间件相关推荐

  1. 优雅的redux异步中间件 redux-effect

    不吹不黑,redux蛮好用.只是有时略显繁琐,叫我定义每一个action.action type.使用时还要在组件上绑定一遍,臣妾做不到呀!下面分享一种个人比较倾向的极简写法,仍有待完善,望讨论. g ...

  2. redux异步action_React躬行记(12)——Redux中间件

    Redux的中间件(Middleware)遵循了即插即用的设计思想,出现在Action到达Reducer之前(如图10所示)的位置.中间件是一个固定模式的独立函数,当把多个中间件像管道那样串联在一起时 ...

  3. Redux 异步数据流-- thunk中间件源码解析

    Thunk 引入背景 这是一个关于Redux异步数据流的故事.引入thunk中间件的完整故事在Redux官方中文文档异步数据流.一句话总结就是:原生Redux只支持同步数据流,所以需要引入中间件(mi ...

  4. 精益 React 学习指南 (Lean React)- 3.4 掌控 redux 异步

    书籍完整目录 3.4 redux 异步 在大多数的前端业务场景中,需要和后端产生异步交互,在本节中,将详细讲解 redux 中的异步方案以及一些异步第三方组件,内容有: redux 异步流 redux ...

  5. redux异步之redux-thunk

    简介 redux-thunk是redux官方退出的一个异步中间件,thunk表示延迟计算的意思 使用中间件之后,我们可以再action构造器中返回一个函数,而不是一个对象 使用 下载 npm inst ...

  6. Redux异步解决方案之Redux-Thunk原理及源码解析

    前段时间,我们写了一篇Redux源码分析的文章,也分析了跟React连接的库React-Redux的源码实现.但是在Redux的生态中还有一个很重要的部分没有涉及到,那就是Redux的异步解决方案.本 ...

  7. 学习 redux 源码整体架构,深入理解 redux 及其中间件原理

    如果觉得内容不错,可以设为星标置顶我的公众号 1. 前言 你好,我是若川.这是学习源码整体架构系列第八篇.整体架构这词语好像有点大,姑且就算是源码整体结构吧,主要就是学习是代码整体结构,不深究其他不是 ...

  8. React学习笔记——redux里中间件Middleware的运行机理

    1.前言 上篇文章中,我们详细介绍了redux的相关知识和如何使用,最后使用中间件Middleware来帮助我们完成异步操作,如下图 上面是很典型的一次 redux 的数据流的过程,在增加了 midd ...

  9. redux异步action_redux-thunk 和 redux-saga 的区别?

    毋庸置疑,如果需要用到 side effect 异步操作,redux-thunk 和 redux-saga 绝对是目前两个最受欢迎的中间件插件.那么他们之间最主要的区别是什么? 这就要首先说一说使用 ...

最新文章

  1. C/C++ 32位/64位 sizeof(数据类型)
  2. java广告无限点击_什么是互联网广告,互联网广告的投放形式都有哪些?
  3. iphonexr电池容量_初代手机到iPhone 12,从电池容量变化看充电功率的发展
  4. pycharm不能输入代码或删除代码
  5. 一文带你了解Java反射机制
  6. [NHFrog]发布第三个版本_NHibernate嵌入式代码生成器
  7. 数学分析原理 定理 6.5
  8. SVN遗漏so文件的解决办法
  9. 分布式系统架构的优缺点
  10. 正己尽己【管理学之六】
  11. Zabbix系列:proxy存储数据表proxy_history
  12. 如何进行产品运营数据分析?
  13. 抓取腾讯动漫app的插图链接
  14. 歌曲《一路生花》的matalb实现(附程序代码,免费)
  15. android系统凭据存储,存储登录凭据android
  16. 泛微Ecology8.0二次开发指导手册
  17. CLIP论文阅读、zero-shot实验、linear prob实验记录
  18. 大话设计模式——外观模式
  19. 解决Ubuntu16.04软件商店无法加载
  20. JavaEE笔记——设计模式

热门文章

  1. python笔记之序列(tuple的基本使用和常用操作)
  2. html读取本地txt_利用MySQL/MariaDB的逻辑缺陷伪造恶意服务端读取客户端文件
  3. linux ftp 配额 quota,linux – vsftpd中的配额?
  4. matlab子函数调用变量,matlab中,怎么样用function自定义函数调用另一个函数名为输入?...
  5. libgdx使用android控件,在Android上使用libGDX中的SQLite
  6. 全局对象_C++全局变量初始化
  7. java改写模式_Java基于状态模式实现的文档编辑模式切换功能实例
  8. python卡尔曼滤波跟踪_使用卡尔曼滤波器以圆周运动跟踪机器人
  9. switch中case后可以是表达式吗_自学C++基础教程【流程控制语句】(switch语句 )...
  10. 适合python爬虫使用的浏览器_python爬虫:使用Selenium模拟浏览器