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,一经查实,立即删除!

相关文章

mysql 存储过程月单拆天单_MySQL之存储过程按月创建表的方法步骤

具体不多说,直接上代码。欢迎一起交流和学习。创建一个按月创建表的存储过程,SQL语句如下:DELIMITER //DROP PROCEDURE IF EXISTS create_table_by_month //CREATE PROCEDURE create_table_by_month()BEGIN#--提前申明变量,后面会用到DECLARE nextMonth …

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

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

开源技术选型手册

目录 第1章 闲话开源社区篇 第2章 Web框架篇 2.1 Struts 2.2 Spring 2.3 Seam 第3章 开源Web服务器 3.1 Apache 3.2 Lighttpd 3.3 Nginx 第4章 应用服务器篇 4.1 JBoss 4.2 Geronimo 4.3 JFox 第5章 开发平台篇(IDE) 5.1 Eclipse 5.2 NetBeans 第6章 动态…

UVA 1156 - Pixel Shuffle(模拟+置换)

UVA 1156 - Pixel Shuffle 题目链接 题意:依据题目中的变换方式,给定一串变换方式,问须要运行几次才干回复原图像 思路:这题恶心的一比,先模拟求出一次变换后的相应的矩阵,然后对该矩阵求出全部循环长度&am…

java 字体名字_JAVA:获取系统中可用的字体的名字

import java.awt.*;public class GetLocalFontFamily{public static void main(String[] agrs){//获取系统中可用的字体的名字GraphicsEnvironment e GraphicsEnvironment.getLocalGraphicsEnvironment();String[] fontName e.getAvailableFontFamilyNames();for(int i 0; i…

document.getElementsByName 标准

document.getElementsByName w3c 标准中: 是获得name属性获得元素 IE 中:是通过ID属性获得元素转载于:https://www.cnblogs.com/chen-lei/archive/2009/12/29/1635343.html

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…

怎么和产品沟通

去别的地方以后和产品交流 随意点 都是给公司打工 你背后骂骂没事,当面和气点 恩 以后碰这样的情况 你就答应着 最后做不完,责任也不是你的 做多少是多少呗 就把情况说清楚 说不保证做完 就行 话不说死 产品告不了状 怎么办哦?切记你是没有…

java循环单链表比较相等_java的循环单链表

packageclink;//循环单链表public classTestClink {public static voidmain(String[] args) {//TODO Auto-generated method stubClink t1 newClink();for(int i 0;i<9;i){t1.insertHead(i);}t1.show();int lent1.getlength();System.out.println(len);System.out.println…

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

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

新手买车的九大原则

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

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

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

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

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

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

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

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

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

PerlRegex堆栈溢出的问题

PerlRegex一直用得很顺手&#xff0c;但今天晚上却老是出现堆栈溢出的问题&#xff0c;调整了Delphi 的Max stack size后&#xff0c;仍然解决不了。 后来发现是正则表达式的问题&#xff0c;具体细节问题还不太清楚 我一般使用“((.|\n)?)”来匹配多行文本&#xff0c;而出现…

《架构之美》学习随笔:好的架构

好的架构是很多因素的结果&#xff0c;包括以下方面&#xff1a; 1、确定进行有意为之的前端设计 2、设计者的素质和经验 3、在开发过程中&#xff0c;保持清晰的设计观点 4、授权团队负责软件的整体设计&#xff0c;而团队也承担起这一责任 5、不要害怕改变设计&#xff1a;没…

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

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

java实现poi导入excel_Java POI实现将导入Excel文件的示例代码

问题描述现需要批量导入数据&#xff0c;数据以Excel形式导入。POI介绍我选择使用的是apache POI。这是有Apache软件基金会开放的函数库&#xff0c;他会提供API给java&#xff0c;使其可以对office文件进行读写。我这里只需要使用其中的Excel部分。实现首先&#xff0c;Excel有…

JS中双引号单引号,转义字符问题!!

investListHtml <div class"targetBlock"> <p> targetClass <span>总金额 <i> loanAmount </i> 元&#xff0c;剩余可投 <i> (loanAmount-collectAmount) </i> 元</span></p> <div class"targetCon…