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 相关文章推荐
设为首页和收藏的Javascript代码(亲测兼容IE,Firefox,chrome等浏览器)
Nov 18 Javascript
原生javaScript做得动态表格(注释写的很清楚)
Dec 29 Javascript
Javascript 正则表达式实现为数字添加千位分隔符
Mar 10 Javascript
JavaScript改变CSS样式的方法汇总
May 07 Javascript
JavaScript 数组some()和filter()的用法及区别
May 20 Javascript
ECMAScript6快速入手攻略
Jul 18 Javascript
angular2倒计时组件使用详解
Jan 12 Javascript
Google 爬虫如何抓取 JavaScript 的内容
Apr 07 Javascript
Centos6.8下Node.js安装教程
May 12 Javascript
使用nvm和nrm优化node.js工作流的方法
Jan 17 Javascript
vue.js的状态管理vuex中store的使用详解
Nov 08 Javascript
JS中准确判断变量类型的方法
Jun 01 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实现将字符串按照指定距离进行分割的方法
2015/03/14 PHP
php微信开发之自定义菜单实现
2016/11/18 PHP
PHP实现json_decode不转义中文的方法
2017/05/20 PHP
使用Javascript和DOM Interfaces来处理HTML
2006/10/09 Javascript
javascript中最常用的继承模式 组合继承
2010/08/12 Javascript
jQuery 瀑布流 绝对定位布局(二)(延迟AJAX加载图片)
2012/05/23 Javascript
使用jquery实现简单的ajax
2013/07/08 Javascript
js实现日期级联效果
2014/01/23 Javascript
Jquery动态替换div内容及动态展示的方法
2015/01/23 Javascript
jQuery使用$.ajax进行异步刷新的方法(附demo下载)
2015/12/04 Javascript
将List对象列表转换成JSON格式的类实现方法
2016/07/04 Javascript
jQuery.ajax 跨域请求webapi设置headers的解决方案
2016/08/08 Javascript
原生js和css实现图片轮播效果
2017/02/07 Javascript
浅谈js中同名函数和同名变量的执行问题
2017/02/12 Javascript
vue学习笔记之vue1.0和vue2.0的区别介绍
2017/05/17 Javascript
微信小程序日期时间选择器使用方法
2018/02/01 Javascript
angularJS实现动态添加,删除div方法
2018/02/27 Javascript
详解使用Next.js构建服务端渲染应用
2018/07/10 Javascript
微信小程序从注册账号到上架(图文详解)
2019/07/17 Javascript
js 下拉菜单点击旁边收起实现(踩坑记)
2019/09/29 Javascript
利用Anaconda简单安装scrapy框架的方法
2018/06/13 Python
python-OpenCV 实现将数组转换成灰度图和彩图
2020/01/09 Python
python3.6.8 + pycharm + PyQt5 环境搭建的图文教程
2020/06/11 Python
Python如何实现Paramiko的二次封装
2021/01/30 Python
全球知名提供各类营养保健品的零售商:Vitamin Shoppe
2016/10/09 全球购物
信息技术专业个人自我评价
2013/12/11 职场文书
幼儿园大班开学教师寄语
2014/04/03 职场文书
槐乡的孩子教学反思
2014/04/27 职场文书
环境保护与污染治理求职信
2014/07/16 职场文书
岗位职责说明书模板
2014/07/30 职场文书
有子女的离婚协议书怎么写(范本)
2014/09/29 职场文书
关于远足的感想
2015/08/10 职场文书
使用Pytorch训练two-head网络的操作
2021/05/28 Python
Nginx动静分离配置实现与说明
2022/04/07 Servers
Windows Server 2008 修改远程登录端口以及配置防火墙
2022/04/28 Servers
mysql sock文件存储了什么信息
2022/07/15 MySQL