redux处理异步action解决方案


Posted in Javascript onMarch 22, 2020

如果没有中间件,store.dispatch只能接收一个普通对象作为action。在处理异步action时,我们需要在异步回调或者promise函数then内,async函数await之后dispatch。

dispatch({
  type:'before-load'
})
fetch('http://myapi.com/${userId}').then({
  response =>dispatch({
      type:'load',
      payload:response
    })
})

这样做确实可以解决问题,特别是在小型项目中,高效,可读性强。 但缺点是需要在组件中写大量的异步逻辑代码,不能将异步过程(例如异步获取数据)与dispatch抽象出来进行复用。而采用类似redux-thunk之类的中间件可以使得dispatch能够接收不仅仅是普通对象作为action。例如:

function load(userId){
  return function(dispatch,getState){
    dispatch({
      type:'before-load'
    })
    fetch('http://myapi.com/${userId}').then({
      response =>dispatch({
        type:'load',
        payload:response
      })
    })
  }  
}
//使用方式
dispatch(load(userId))

使用中间件可以让你采用自己方便的方式dispatch异步action,下面介绍常见的三种。

1. 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;

2. redux-promise

使用redux-promise可以将action或者action的payload写成promise形式。 源码:

export default function promiseMiddleware({ dispatch }) {
 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 }))
     .catch(error => {
      dispatch({ ...action, payload: error, error: true });
      return Promise.reject(error);
     })
   : next(action);
 };
}

3. Redux-saga

redux-saga 是一个用于管理应用程序 Side Effect(副作用,例如异步获取数据,访问浏览器缓存等)的 library,它的目标是让副作用管理更容易,执行更高效,测试更简单,在处理故障时更容易

3.1 基本使用

import { call, put, takeEvery, takeLatest} from 'redux-saga/effects';

//复杂的异步流程操作
function* fetchUser(action){
 try{
  const user = yield call(API.fetchUser, action.payload);
  yield put({type:"USER_FETCH_SUCCEEDED",user:user})
 }catch(e){
  yield put({type:"USER_FETCH_FAILED",message:e.message})
 }
}

//监听dispatch,调用相应函数进行处理
function* mainSaga(){
 yield takeEvery("USER_FETCH_REQUESTED",fetchUser);
}

//在store中注入saga中间件
import {createStore,applyMiddleware} from 'redux';
import createSagaMiddleware from 'redux-saga';

import reducer from './reducers';
import mainSaga from './mainSaga';
const sagaMiddleware = createSagaMiddleware();

const store = createStore(reducer,initalState,applyMiddleware(sagaMiddleware));

sagaMiddleware.run(mainSaga)

3.2 声明式effects,便于测试

为了测试方便,在generator中不立即执行异步调用,而是使用call、apply等effects创建一条描述函数调用的对象,saga中间件确保执行函数调用并在响应被resolve时恢复generator。

function* fetchProducts() {
 const products = yield Api.fetch('/products')
 dispatch({ type: 'PRODUCTS_RECEIVED', products })
}

//便于测试
function* fetchProducts() {
 const products = yield call(Api.fetch, '/products')
 //便于测试dispatch
 yield put({ type: 'PRODUCTS_RECEIVED', products })
 // ...
}
// Effect -> 调用 Api.fetch 函数并传递 `./products` 作为参数
{
 CALL: {
  fn: Api.fetch,
  args: ['./products'] 
 }
}

3.3 构建复杂的控制流

saga可以通过使用effect创建器、effect组合器、saga辅助函数来构建复杂的控制流。

effect创建器:

  • take:阻塞性effect,等待store中匹配的action或channel中的特定消息。
  • put:非阻塞性effect,用来命令 middleware 向 Store 发起一个 action。
  • call:阻塞性effect,用来命令 middleware 以参数 args 调用函数 fn。
  • fork:非阻塞性effect,用来命令 middleware 以 非阻塞调用 的形式执行 fn
  • select:非阻塞性effect,用来命令 middleware 在当前 Store 的 state 上调用指定的选择器

effect组合器:

race:阻塞性effect:用来命令 middleware 在多个 Effect 间运行 竞赛(Race)

function fetchUsersSaga { 
 const { response, cancel } = yield race({ 
 response: call(fetchUsers), 
 cancel: take(CANCEL_FETCH) 
 }) 
}

all: 当 array 或 object 中有阻塞型 effect 的时候阻塞,用来命令 middleware 并行地运行多个 Effect,并等待它们全部完成

function* mySaga() { 
 const [customers, products] = yield all([ 
 call(fetchCustomers), 
 call(fetchProducts) 
 ]) 
}

effect辅助函数:

takeEvery:非阻塞性effect,在发起(dispatch)到 Store 并且匹配 pattern 的每一个 action 上派生一个 saga

const takeEvery = (patternOrChannel, saga, ...args) => fork(function*() { 
while (true) { 
const action = yield take(patternOrChannel) 
yield fork(saga, ...args.concat(action)) 
} 
  })

takeLatest:非阻塞性,在发起到 Store 并且匹配 pattern 的每一个 action 上派生一个 saga。并自动取消之前所有已经启动但仍在执行中的 saga 任务。

const takeLatest = (patternOrChannel, saga, ...args) => fork(function*() { 
  let lastTask 
  while (true) { 
  const action = yield take(patternOrChannel) 
  if (lastTask) { 
  yield cancel(lastTask) // 如果任务已经结束,cancel 则是空操作 
  } 
  lastTask = yield fork(saga, ...args.concat(action)) 
  } 
 })

throttle:非阻塞性,在 ms 毫秒内将暂停派生新的任务

const throttle = (ms, pattern, task, ...args) => fork(function*() { 
  const throttleChannel = yield actionChannel(pattern) 
  ​ 
  while (true) { 
  const action = yield take(throttleChannel) 
  yield fork(task, ...args, action) 
  yield delay(ms) 
  } 
  })

到此这篇关于redux处理异步action解决方案的文章就介绍到这了,更多相关redux 异步action内容请搜索三水点靠木以前的文章或继续浏览下面的相关文章希望大家以后多多支持三水点靠木!

Javascript 相关文章推荐
用js实现随机返回数组的一个元素
Aug 13 Javascript
JS location几个方法小姐
Jul 09 Javascript
jQuery实现TAB风格的全国省份城市滑动切换效果代码
Aug 24 Javascript
JavaScript Math.round() 方法
Dec 18 Javascript
基于socket.io+express实现多房间聊天
Mar 17 Javascript
JS使用onerror捕获异常示例
Aug 03 Javascript
利用JavaScript缓存远程窃取Wi-Fi密码的思路详解
Nov 05 Javascript
vue的.vue文件是怎么run起来的(vue-loader)
Dec 10 Javascript
JavaScript实现的拼图算法分析
Feb 13 Javascript
微信小程序绘制图片发送朋友圈
Jul 25 Javascript
微信小程序如何加载数据库真实数据的实现
Mar 04 Javascript
AudioContext 实现音频可视化(web技术分享)
Feb 24 Javascript
JS+CSS实现3D切割轮播图
Mar 21 #Javascript
vue-autoui自匹配webapi的UI控件的实现
Mar 20 #Javascript
jQuery实现中奖播报功能(让文本滚动起来) 简单设置数值即可
Mar 20 #jQuery
微信小程序保持session会话的方法
Mar 20 #Javascript
微信小程序后端无法保持session的原因及解决办法问题
Mar 20 #Javascript
js 闭包深入理解与实例分析
Mar 19 #Javascript
JS一次前端面试经历记录
Mar 19 #Javascript
You might like
PHP函数实现分页含文本分页和数字分页
2014/10/23 PHP
Laravel 自带的Auth验证登录方法
2019/09/30 PHP
一个报数游戏js版(约瑟夫环问题)
2010/08/05 Javascript
JS中confirm,alert,prompt函数区别分析
2011/01/17 Javascript
JS自定义功能函数实现动态添加网址参数修改网址参数值
2013/08/02 Javascript
jquery实现在网页指定区域显示自定义右键菜单效果
2015/08/25 Javascript
JavaScript获取当前运行脚本文件所在目录的方法
2016/02/03 Javascript
js 创建对象 经典模式全面了解
2016/08/16 Javascript
chrome浏览器如何断点调试异步加载的JS
2016/09/05 Javascript
清空元素html("") innerHTML="" 与 empty()的区别和应用(推荐)
2017/08/14 Javascript
基于Node.js实现压缩和解压缩的方法
2018/02/13 Javascript
跨域请求两种方法 jsonp和cors的实现
2018/11/11 Javascript
JS实现TITLE悬停长久显示效果完整示例
2020/02/11 Javascript
Vue 中如何将函数作为 props 传递给组件的实现代码
2020/05/12 Javascript
python的re正则表达式实例代码
2018/01/24 Python
Python网页正文转换语音文件的操作方法
2018/12/09 Python
Python文件如何引入?详解引入Python文件步骤
2018/12/10 Python
Python 计算任意两向量之间的夹角方法
2019/07/05 Python
python递归法实现简易连连看小游戏
2020/03/25 Python
Django 实现admin后台显示图片缩略图的例子
2019/07/28 Python
PyQT5 emit 和 connect的用法详解
2019/12/13 Python
Python如何截图保存的三种方法(小结)
2020/09/01 Python
Python获取android设备cpu和内存占用情况
2020/11/15 Python
解决python 执行shell命令无法获取返回值的问题
2020/12/05 Python
Window10上Tensorflow的安装(CPU和GPU版本)
2020/12/15 Python
基于CSS3实现图片模糊过滤效果
2015/11/19 HTML / CSS
全球性的在线时尚男装零售商:boohooMAN
2016/12/17 全球购物
电钳专业个人求职信
2014/01/04 职场文书
护理专业学生职业生涯规划范文
2014/03/11 职场文书
欢迎领导检查标语
2014/06/27 职场文书
鲁迅故居导游词
2015/02/05 职场文书
文明礼仪主题班会
2015/08/13 职场文书
读《工匠精神》有感:热爱工作,精益求精
2019/12/28 职场文书
Windows下使用Nginx+Tomcat做负载均衡的完整步骤
2021/03/31 Servers
Python list去重且保持原顺序不变的方法
2021/04/03 Python
浅谈golang 中time.After释放的问题
2021/05/05 Golang