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 相关文章推荐
关于捕获用户何时点击window.onbeforeunload的取消事件
Mar 06 Javascript
JS实现兼容火狐及IE iframe onload属性的遮罩层隐藏及显示效果
Aug 23 Javascript
JavaScript实现点击按钮复制指定区域文本(推荐)
Nov 25 Javascript
JS多物体实现缓冲运动效果示例
Dec 20 Javascript
JS jQuery使用正则表达式去空字符的简单实现代码
May 20 jQuery
微信小程序动态添加分享数据
Jun 14 Javascript
利用node.js实现自动生成前端项目组件的方法详解
Jul 12 Javascript
JavaScript执行环境及作用域链实例分析
Aug 01 Javascript
微信小程序使用wxParse解析html的方法示例
Jan 17 Javascript
jquery实现下载图片功能
Jul 18 jQuery
Bootstrap table 实现树形表格联动选中联动取消功能
Sep 30 Javascript
vuex state中的数组变化监听实例
Nov 06 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
ThinkPHP3.1新特性之G方法的使用
2014/06/19 PHP
php魔术函数__call()用法实例分析
2015/02/13 PHP
PHP和Mysql中转UTF8编码问题汇总
2015/10/10 PHP
thinkphp项目如何自定义微信分享描述内容
2017/02/20 PHP
laravel实现按时间日期进行分组统计方法示例
2019/03/23 PHP
js 颜色选择器(兼容firefox)
2009/03/05 Javascript
Js动态添加复选框Checkbox的实例方法
2013/04/08 Javascript
js获取系统的根路径实现介绍
2013/09/08 Javascript
Javascript数组操作函数总结
2015/02/05 Javascript
jQuery图片前后对比插件beforeAfter用法示例【附demo源码下载】
2016/09/20 Javascript
Web纯前端“旭日图”实现元素周期表
2017/03/10 Javascript
JS操作时间 - UNIX时间戳的简单介绍(必看篇)
2017/08/16 Javascript
jQuery UI Draggable + Sortable 结合使用(实例讲解)
2017/09/07 jQuery
React学习之事件绑定的几种方法对比
2017/09/24 Javascript
vue.js 获取select中的value实例
2018/03/01 Javascript
微信小程序画布圆形进度条显示效果
2020/11/17 Javascript
electron + vue项目实现打印小票功能及实现代码
2018/11/25 Javascript
微信小程序中为什么使用var that=this
2019/08/27 Javascript
微信小程序canvas实现签名功能
2021/01/19 Javascript
[03:48]显微镜下的DOTA2第四期——TP动作
2014/06/20 DOTA
[02:16]2018年度CS GO最具人气选手-完美盛典
2018/12/16 DOTA
pygame学习笔记(3):运动速率、时间、事件、文字
2015/04/15 Python
微信跳一跳游戏python脚本
2020/04/01 Python
对Python协程之异步同步的区别详解
2019/02/19 Python
Python用字典构建多级菜单功能
2019/07/11 Python
python爬虫增加访问量的方法
2019/08/22 Python
python manage.py runserver流程解析
2019/11/08 Python
Python openpyxl模块实现excel读写操作
2020/06/30 Python
python 爬虫网页登陆的简单实现
2020/11/30 Python
详解使用双缓存解决Canvas clearRect引起的闪屏问题
2019/04/29 HTML / CSS
英国在线玫瑰专家:InterRose
2019/12/01 全球购物
用JAVA实现一种排序,JAVA类实现序列化的方法(二种)
2014/04/23 面试题
历史专业毕业生的自我鉴定
2013/11/15 职场文书
MySQL学习总结-基础架构概述
2021/04/05 MySQL
python实现大文本文件分割成多个小文件
2021/04/20 Python
vue判断按钮是否可以点击
2022/04/09 Vue.js