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中字符问题(二进制/十进制/十六进制及ASCII码之间的转换)
Nov 03 Javascript
JS array 数组详解
Mar 22 Javascript
Javascript结合css实现网页换肤功能
Nov 02 Javascript
JQuery实现的图文自动轮播效果插件
Jun 19 Javascript
JavaScript jquery及AJAX小结
Jan 24 Javascript
关于JS中的apply,call,bind的深入解析
Apr 05 Javascript
微信小程序中单位rpx和rem的使用
Dec 06 Javascript
jQuery EasyUI 为Combo,Combobox添加清除值功能的实例
Apr 13 jQuery
关于js陀螺仪的理解分析
Apr 11 Javascript
js图片无缝滚动插件使用详解
May 26 Javascript
微信小程序 拍照或从相册选取图片上传代码实例
Aug 28 Javascript
Vue +WebSocket + WaveSurferJS 实现H5聊天对话交互的实例
Nov 18 Vue.js
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 变量定义方法
2009/06/14 PHP
PHP通过iconv将字符串从GBK转换为UTF8字符集
2011/07/18 PHP
php采集中国代理服务器网的方法
2015/06/16 PHP
PHP脚本自动识别验证码查询汽车违章
2016/12/20 PHP
Redis构建分布式锁
2017/03/28 PHP
PHP中常用的三种设计模式详解【单例模式、工厂模式、观察者模式】
2019/06/14 PHP
javascript 清除输入框中的数据
2009/04/13 Javascript
海量经典的jQuery插件集合
2010/01/12 Javascript
javascript 正则表达式相关应介绍
2012/11/27 Javascript
sencha touch 模仿tabpanel导航栏TabBar的实例代码
2013/10/24 Javascript
jQuery使用append在html元素后同时添加多项内容的方法
2015/03/26 Javascript
jQuery实现图片左右滚动特效
2020/04/20 Javascript
jQuery设置聚焦并使光标位置在文字最后的实现方法
2016/08/02 Javascript
浅析如何利用JavaScript进行语音识别
2016/10/27 Javascript
localStorage实现便签小程序
2016/11/28 Javascript
bootstrap table实现单击单元格可编辑功能
2017/03/28 Javascript
微信小程序中使用javascript 回调函数
2017/05/11 Javascript
Vue中使用clipboard实现复制功能
2018/09/05 Javascript
Intellij IDEA搭建vue-cli项目的方法步骤
2018/10/20 Javascript
js canvas实现橡皮擦效果
2018/12/20 Javascript
在node中使用jwt签发与验证token的方法
2019/04/03 Javascript
微信小程序开发之map地图组件定位并手动修改位置偏差
2019/08/17 Javascript
JavaScript本地储存:localStorage、sessionStorage、cookie的使用
2020/10/13 Javascript
python 判断自定义对象类型
2009/03/21 Python
Python删除指定目录下过期文件的2个脚本分享
2014/04/10 Python
python excel使用xlutils类库实现追加写功能的方法
2018/05/02 Python
Python查看微信撤回消息代码
2018/06/07 Python
Python定义函数时参数有默认值问题解决
2019/12/19 Python
Python3 io文本及原始流I/O工具用法详解
2020/03/23 Python
CSS3 制作旋转的大风车(充满童年回忆)
2013/01/30 HTML / CSS
HTML5梦幻之旅——炫丽的流星雨效果实现过程
2013/08/06 HTML / CSS
kmart凯马特官网:美国最大的打折零售商和全球最大的批发商之一
2016/11/17 全球购物
英国最大的在线床超市:Bed Star
2019/01/24 全球购物
项目总经理岗位职责
2014/02/14 职场文书
《穷人》教学反思
2014/04/08 职场文书
无锡灵山大佛导游词
2015/02/09 职场文书