Reflux系列01:异步操作经验小结

写在前面

在实际项目中,应用往往充斥着大量的异步操作,如ajax请求,定时器等。一旦应用涉及异步操作,代码便会变得复杂起来。在flux体系中,让人困惑的往往有几点:

  1. 异步操作应该在actions还是store中进行?
  2. 异步操作的多个状态,如pending(处理中)、completed(成功)、failed(失败),该如何拆解维护?
  3. 请求参数校验:应该在actions还是store中进行校验?校验的逻辑如何跟业务逻辑本身进行分离?

本文从简单的同步请求讲起,逐个对上面3个问题进行回答。一家之言并非定则,读者可自行判别。

本文适合对reflux有一定了解的读者,如尚无了解,可先行查看 官方文档 。本文所涉及的代码示例,可在 此处下载。

Sync Action:同步操作

同步操作比较简单,没什么好讲的,直接上代码可能更直观。

var Reflux = require('reflux');var TodoActions = Reflux.createActions({addTodo: {sync: true}
});var state = [];
var TodoStore = Reflux.createStore({listenables: [TodoActions],onAddTodo: function(text){state.push(text);this.trigger(state);},getState: function(){return state;}
});TodoStore.listen(function(state){console.log('state is: ' + state);  
});
TodoActions.addTodo('起床');
TodoActions.addTodo('吃早餐');
TodoActions.addTodo('上班');

看下运行结果

➜  examples git:(master) ✗ node 01-sync-actions.js
state is: 起床
state is: 起床,吃早餐
state is: 起床,吃早餐,上班

Async Action:在store中处理

下面是个简单的异步操作的例子。这里通过addToServer这个方法来模拟异步请求,并通过isSucc字段来控制请求的状态为成功还是失败

可以看到,这里对前面例子中的state进行了一定的改造,通过state.status来保存请求的状态,包括:

  • pending:请求处理中
  • completed:请求处理成功
  • failed:请求处理失败
var Reflux = require('reflux');/*** @param {String} options.text * @param {Boolean} options.isSucc 是否成功* @param {Function} options.callback 异步回调* @param {Number} options.delay 异步延迟的时间*/
var addToServer = function(options){var ret = {code: 0, text: options.text, msg: '添加成功 :)'};if(!options.isSucc){ret = {code: -1, msg: '添加失败!'};}setTimeout(function(){options.callback && options.callback(ret);}, options.delay);
};var TodoActions = Reflux.createActions(['addTodo']);var state = {items: [],status: ''
};var TodoStore = Reflux.createStore({init: function(){state.items.push('睡觉');},listenables: [TodoActions],onAddTodo: function(text, isSucc){var that = this;state.status = 'pending';that.trigger(state);addToServer({text: text,isSucc: isSucc,delay: 500,callback: function(ret){if(ret.code===0){state.status = 'success';state.items.push(text);}else{state.status = 'error';}that.trigger(state);}});},getState: function(){return state;}
});TodoStore.listen(function(state){console.log('status is: ' + state.status + ', current todos is: ' + state.items);
});TodoActions.addTodo('起床', true);
TodoActions.addTodo('吃早餐', false);
TodoActions.addTodo('上班', true);

看下运行结果:

➜  examples git:(master) ✗ node 02-async-actions-in-store.js 
status is: pending, current todos is: 睡觉
status is: pending, current todos is: 睡觉
status is: pending, current todos is: 睡觉
status is: success, current todos is: 睡觉,起床
status is: error, current todos is: 睡觉,起床
status is: success, current todos is: 睡觉,起床,上班

Async Action:在store中处理 潜在的问题

首先,祭出官方flux架构示意图,相信大家对这张图已经很熟悉了。flux架构最大的特点就是单向数据流,它的好处在于 可预测易测试

一旦将异步逻辑引入store,单向数据流被打破,应用的行为相对变得难以预测,同时单元测试的难度也会有所增加。

enter image description here

ps:在大部分情况下,将异步操作放在store里,简单粗暴有效,反而可以节省不少代码,看着也直观。究竟放在actions、store里,笔者是倾向于放在actions里的,读者可自行斟酌。

毕竟,社区对这个事情也还在吵个不停。。。

Async 操作:在actions中处理

还是前面的例子,稍作改造,将异步的逻辑挪到actions里,二话不说上代码。

reflux是比较接地气的flux实现,充分考虑到了异步操作的场景。定义action时,通过asyncResult: true标识:

  1. 操作是异步的。
  2. 异步操作是分状态(生命周期)的,默认的有completedfailed。可以通过children参数自定义请求状态。
  3. 在store里通过类似onAddTodoonAddTodoCompletedonAddTodoFailed对请求的不同的状态进行处理。
var Reflux = require('reflux');/*** @param {String} options.text * @param {Boolean} options.isSucc 是否成功* @param {Function} options.callback 异步回调* @param {Number} options.delay 异步延迟的时间*/
var addToServer = function(options){var ret = {code: 0, text: options.text, msg: '添加成功 :)'};if(!options.isSucc){ret = {code: -1, msg: '添加失败!'};}setTimeout(function(){options.callback && options.callback(ret);}, options.delay);
};var TodoActions = Reflux.createActions({addTodo: {asyncResult: true}
});TodoActions.addTodo.listen(function(text, isSucc){var that = this;addToServer({text: text,isSucc: isSucc,delay: 500,callback: function(ret){if(ret.code===0){that.completed(ret);}else{that.failed(ret);}}});
});var state = {items: [],status: ''
};var TodoStore = Reflux.createStore({init: function(){state.items.push('睡觉');},listenables: [TodoActions],onAddTodo: function(text, isSucc){var that = this;state.status = 'pending';this.trigger(state);},onAddTodoCompleted: function(ret){state.status = 'success';state.items.push(ret.text);this.trigger(state);},onAddTodoFailed: function(ret){state.status = 'error';this.trigger(state);},getState: function(){return state;}
});TodoStore.listen(function(state){console.log('status is: ' + state.status + ', current todos is: ' + state.items);
});TodoActions.addTodo('起床', true);
TodoActions.addTodo('吃早餐', false);
TodoActions.addTodo('上班', true);

运行,看程序输出

➜  examples git:(master) ✗ node 03-async-actions-in-action.js 
status is: pending, current todos is: 睡觉
status is: pending, current todos is: 睡觉
status is: pending, current todos is: 睡觉
status is: success, current todos is: 睡觉,起床
status is: error, current todos is: 睡觉,起床
status is: success, current todos is: 睡觉,起床,上班

Async Action:参数校验

前面已经示范了如何在actions里进行异步请求,接下来简单演示下异步请求的前置步骤:参数校验。

预期中的流程是:

流程1:参数校验 --> 校验通过 --> 请求处理中 --> 请求处理成功(失败)
流程2:参数校验 --> 校验不通过 --> 请求处理失败

预期之外:store.onAddTodo 触发

直接对上一小节的代码进行调整。首先判断传入的text参数是否是字符串,如果不是,直接进入错误处理。

var Reflux = require('reflux');/*** @param {String} options.text * @param {Boolean} options.isSucc 是否成功* @param {Function} options.callback 异步回调* @param {Number} options.delay 异步延迟的时间*/
var addToServer = function(options){var ret = {code: 0, text: options.text, msg: '添加成功 :)'};if(!options.isSucc){ret = {code: -1, msg: '添加失败!'};}setTimeout(function(){options.callback && options.callback(ret);}, options.delay);
};var TodoActions = Reflux.createActions({addTodo: {asyncResult: true}
});TodoActions.addTodo.listen(function(text, isSucc){var that = this;if(typeof text !== 'string'){that.failed({ret: 999, text: text, msg: '非法参数!'});return;}addToServer({text: text,isSucc: isSucc,delay: 500,callback: function(ret){if(ret.code===0){that.completed(ret);}else{that.failed(ret);}}});
});var state = {items: [],status: ''
};var TodoStore = Reflux.createStore({init: function(){state.items.push('睡觉');},listenables: [TodoActions],onAddTodo: function(text, isSucc){var that = this;state.status = 'pending';this.trigger(state);},onAddTodoCompleted: function(ret){state.status = 'success';state.items.push(ret.text);this.trigger(state);},onAddTodoFailed: function(ret){state.status = 'error';this.trigger(state);},getState: function(){return state;}
});TodoStore.listen(function(state){console.log('status is: ' + state.status + ', current todos is: ' + state.items);
});// 非法参数
TodoActions.addTodo(true, true);

运行看看效果。这里发现一个问题,尽管参数校验不通过,但store.onAddTodo 还是被触发了,于是打印出了status is: pending, current todos is: 睡觉

而按照我们的预期,store.onAddTodo是不应该触发的。

➜  examples git:(master) ✗ node 04-invalid-params.js 
status is: pending, current todos is: 睡觉
status is: error, current todos is: 睡觉

shouldEmit 阻止store.onAddTodo触发

好在reflux里也考虑到了这样的场景,于是我们可以通过shouldEmit来阻止store.onAddTodo被触发。关于这个配置参数的使用,可参考文档。

看修改后的代码

var Reflux = require('reflux');/*** @param {String} options.text * @param {Boolean} options.isSucc 是否成功* @param {Function} options.callback 异步回调* @param {Number} options.delay 异步延迟的时间*/
var addToServer = function(options){var ret = {code: 0, text: options.text, msg: '添加成功 :)'};if(!options.isSucc){ret = {code: -1, msg: '添加失败!'};}setTimeout(function(){options.callback && options.callback(ret);}, options.delay);
};var TodoActions = Reflux.createActions({addTodo: {asyncResult: true}
});TodoActions.addTodo.shouldEmit = function(text, isSucc){if(typeof text !== 'string'){this.failed({ret: 999, text: text, msg: '非法参数!'});return false;}return true;
};TodoActions.addTodo.listen(function(text, isSucc){var that = this;addToServer({text: text,isSucc: isSucc,delay: 500,callback: function(ret){if(ret.code===0){that.completed(ret);}else{that.failed(ret);}}});
});var state = {items: [],status: ''
};var TodoStore = Reflux.createStore({init: function(){state.items.push('睡觉');},listenables: [TodoActions],onAddTodo: function(text, isSucc){var that = this;state.status = 'pending';this.trigger(state);},onAddTodoCompleted: function(ret){state.status = 'success';state.items.push(ret.text);this.trigger(state);},onAddTodoFailed: function(ret){state.status = 'error';this.trigger(state);},getState: function(){return state;}
});TodoStore.listen(function(state){console.log('status is: ' + state.status + ', current todos is: ' + state.items);
});// 非法参数
TodoActions.addTodo(true, true);
setTimeout(function(){TodoActions.addTodo('起床', true);
}, 100)

再次运行看看效果。通过对比可以看到,当shouldEmit返回false,就达到了之前预期的效果。

➜  examples git:(master) ✗ node 05-invalid-params-shouldEmit.js 
status is: error, current todos is: 睡觉
status is: pending, current todos is: 睡觉
status is: success, current todos is: 睡觉,起床

写在后面

flux的实现细节存在不少争议,而针对文中例子,reflux的设计比较灵活,同样是使用reflux,也可以有多种实现方式,具体全看判断取舍。

最后,欢迎交流。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/262219.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

python转换成c语言_把python转成c

广告关闭 腾讯云11.11云上盛惠 ,精选热门产品助力上云,云服务器首年88元起,买的越多返的越多,最高返5000元! 推荐使用腾讯云 api 配套的7种常见的编程语言 sdk,已经封装了签名和请求过程,均已开…

python逐行写入csv_python将列表按行写入csv

原博文 2020-05-14 13:17 − import csv rows2 [abc1/ab1c,N] for n in range(10): f open("ok.csv", a,newline) writer csv.writer(f) writer.writerow(rows2) f.cl...02349 相关推荐 2019-12-18 14:42 − 逗号分隔值(Comma-Separated Values&#x…

第8章4节《MonkeyRunner源码剖析》MonkeyRunner启动运行过程-启动 8

这个方法所做的事情就是:446行: 首先通过Device类的executeShellCommand方法发送类似”adb shell getprop”的命令去获得所有支持的系统属性,这个方法最终调用的是AdbHelper类的executeShellCommand方法,它会接收一个专门用来对指定shell命令如getprop的…

新手买车的九大原则

汽车俨然已经成为了人们生活中不可缺少的助手。放眼市场,汽车的品牌、车型、颜色、配置,划分的类别太多太多,让众多爱车友人在购车时眼花缭乱。由于厂家、商家片面的宣传和误导以及购车者思想上或这样或那样的误区,使众人面对购车…

python实现链表的删除_Python垃圾回收机制

python作为一门解释型语言,以代码简洁易懂著称。我们可以直接对名称赋值,而不必声明类型。名称类型的确定、内存空间的分配与释放都是由python解释器在运行时进行的。python这一自动管理内存功能极大的减小了程序员负担,这也是成就python自身的重要原因之…

第一章:The Missing Code Library--2.合法化输入

合法化输入:只允许数字和字母 用户常常会忽略掉说明,并且输入错误的数据。作为一个Shell脚本开发人员,你需要拦截并纠正这些错误。 典型情况是,你或许会遇见文件名或是数据库的键。你提示用户要输入一个全部由大小写字母和数字…

java 缓存清理echo_“kill -9”一时爽,秋后算账泪两行

原创:小姐姐味道(微信公众号ID:xjjdog),欢迎分享,转载请保留出处。任何不保留此声明的转载都是抄袭。kill是杀死的意思,带有主动的意味。鉴于master、slave这样的名词,需要在计算机软件中进行整改&#xff…

解决stackoverflow打开慢不能注册登录

http://blog.csdn.net/dream_an/article/details/50280977 解决stackoverflow打开慢不能注册登录 标签: stack overflowfirefox扩展打不开 2015-12-13 09:16 131人阅读 评论(2) 收藏 举报 分类:综合(6) 作者同类文章X版权声明&a…

halcon 旋转_HALCON高级篇:3D相机标定(3/3)

访问标定结果算子calibrate_cameras的主要结果由相机内参和每一张图像标定板的位姿组成。算子将它们存储在标定数据模型中,可以用算子get_calib_data来访问它们。相机外参并不能直接被获取,因为所需的世界坐标系统的信息没有存储在标定数据模型中。然而&…

什么叫n+1次select查询问题?

在Session的缓存中存放的是相互关联的对象图。默认情况下,当Hibernate从数据库中加载Customer对象时,会同时加载所有关联的Order对象。以Customer和Order类为例,假定ORDERS表的CUSTOMER_ID外键允许为null,图1列出了CUSTOMERS表和O…

OGRE 入门 二、Basic Tutorial 1 : An introduction to the most basic Ogre constructs

1. 下载源代码及脚本 这里有一个‘Convenient All-In-One’ 版的框架。 2. 创建场景 解压clean_ogre_cmake_project.zip,修改TutorialApplication.cpp中的函数: 1 void TutorialApplication::createScene(void)2 {3 // create your scene here :)4 …

React Native 重新建项目遇到的一些问题

1、基本上一句话,就是本地的node太旧了,跟不上React_Native的节奏,所以需要更新node,但是单纯的更新node丫丫竟然不让我跟,因为是用Homebrew来管理的,所以先update了下brew brew update && brew up…

picACG本地缓存目录_手机上本地存储的哪些文件、文件夹不能删?

手机、电脑随着不断的使用,系统本身、系统工具、第三方应用都会不断产生一些临时文件和垃圾文件。手机产生的垃圾文件更是显得杂乱无章,虽然可以使用系统自带或第三方应用来清理垃圾,但在本地存储还是有大量的文件夹或文件是可以手动删除的。…

python元组和列表的联系_Python元组与列表的区别和联系?

1.元组和列表比较相似,不过它们之间也有着不同: (1)列表:一个大仓库,你可以随时往里边添加和删除任何东西。 (2)元组:封闭的列表,一旦定义,就不可…

pytorch自带网络_PyTorch机器学习笔记(1)整好环境

2020年1月1日炼丹第0步,装好环境系统 Ubuntu 18.04 LTS先装上conda(自带大多数科学计算基础包,以及比较优秀的包管理系统):国内网络环境建议到清华大学镜像站下载:Tsinghua Open Source Mirror​mirror.tun…

Effective C# Item22:使用事件定义外发接口

事件为类型定义了外发接口,C#的事件是建立在委托的基础上的,委托为事件处理器提供了类型安全的函数签名。 委托要比事件的使用范围广泛,我们可以把事件看做是一种经过了封装的委托,专门用于事件驱动模型。你可以在客户代码中直接调…

python画菱形的代码_Python打印“菱形”星号代码方法

本人是一名python初学者,刚刚看到一道有趣的python问题,“用python如何在编译器中打印出菱形图案?” 因此决定尝试一下,代码不多,仅供参考。 代码 def printStar(intNum): s "*" spaceLength intNum block…

Ubuntu 14.04.3 LTS 配置 DNS Server

我们目的是用一台局域网机器完成 192.168.1.113 <-->cloudshield.com的解析&#xff0c;指定A记录和CNAME; 0.关于Ubuntu 14.04.2 LTS 下载、安装、更新这里就直接跳过了; 1.下载安装工具 bind9 sudo apt-get install bind9 DNS 配置文件在/etc/bind 目录中。安装bind9后…

php代码加注释_怎么在php中添加注释

注释在写代码的过程中非常重要&#xff0c;好的注释能让你的代码读起来更轻松&#xff0c;在写代码的时候一定要注意注释的规范。php里面常见的几种注释方式&#xff1a;1.文件头的注释&#xff0c;介绍文件名&#xff0c;功能以及作者版本号等信息/***文件名简单介绍**文件功能…

数据库整理

转载于:https://www.cnblogs.com/cuikang/p/5131531.html