详解js的异步编程技术的方法


Posted in Javascript onFebruary 09, 2017

基于浏览器的事件轮询机制(以及Node.js中的事件轮询机制),JavaScript常常会运行在异步环境中。由于JavaScript本身语言的特性(不需要程序员操控线程/进程),在js中解决异步化编程的方法就显得相当重要。可以说一个完整的项目中,js开发人员是不可能不面对异步操作的。本文将详细介绍几种经典JavaScript异步编程串行化方法,同时也将简单介绍一下ES6提供的Promise顺序执行方法。

一.回调函数

(1)经典回调函数方式:嵌套内联函数

假设我们有一个ajax()方法,他接收一个url参数,向该地址发起一个异步请求,在请求结束时执行第二个参数—一个回调函数:

ajax(url,function(result){
 console.log(result);
});

可以说这种方式几乎是每个前端开发人员都用过的回调函数方式,有了这样的回调机制,开发人员就不用编写类似下面这样的代码来推测服务器请求什么时候返回:

var result=ajax(url);
setTimeout(function(result){
 console.log(result);
},400);

大家应该能明白我此处想表达的意思。我们设置了一个延迟400毫秒的定时器,来假设我们发出的ajax请求会在400毫秒之内完成。否则,我们将会操作一个undefined的result。

但是有一个问题随着项目的扩大渐渐浮现出来:如果场景需要我们多层嵌套回调函数,代码将变得难以阅读和维护:

ajax(url0,function(result0){
 ajax(result0.url1,function(result1){
  ajax(result1.url2,function(result2){
   console.log(result2);
  });
 });
});

(2)调用外部函数

为了解决内联回调函数暴露出来的代码混乱问题,我们引入外部函数调用来解决类似问题:

function handle2(result){
 console.log(result);
}
function handle1(result){
 ajax(result.url,function(result){
  handle2(result);
 });
}
ajax(url,function(result){
 handle1(result);
});

通过这种拆分内联函数,来调用外部函数的优化方法,能极大的保持代码的简洁性。

二.制定回调管理器

观察流行的JavaScript流程控制工具,例如Nimble、Step、Seq,我们会学习到一种简洁的设计模式:通过回调管理器来控制异步JavaScript执行流程,下面是一个典型的回调管理器的关键代码示例:

var Flow={};
//设置next方法,在上一个方法完成时调用下一个方法
Flow.next=function(){
 if(this.stack[0]){
  //弹出方法栈中的第一个方法,并执行他
  this.stack.shift()();
 }
};
//设置series方法,接收一个函数数组,并按序执行
Flow.series=function(arr){
 this.stack=arr;
 this.next();
};

//通过Flow.series我们能够控制传入的函数的执行顺序
Flow.series([
  function(){
   //do something
   console.log(1);
   Flow.next();
  },
  function(next){
   //do something
   console.log(2);
   Flow.next();
  }
]);

我们初始化了一个Flow控制器,为他设计了series和next两个函数属性。在我们编写的业务方法内部,在方法结尾处通过调用Flow.next()的方式来顺序触发下一个方法;通过执行series方法来顺序执行异步函数。这种通过核心控制器来管理异步函数调用的方式简化了我们的编程过程,让开发人员能够投入更多精力在业务逻辑上。

三.全局标记控制

(1)简单计数器控制

也许上面介绍的异步方法仍然不能满足实际开发中的业务场景:假设我们有a(),b(),c()三个方法,a和b没有依赖关系,可以异步进行。但是c必须在a和b都完成之后才能触发。为满足这样的逻辑实现,我们加入一个全局计数器来控制代码的执行流程:

var flag=2;
var aValue,bValue;
function a(){
 aValue=1;
 flag--;
 c();
}
function b(){
 setTimeout(function(){
  bValue=2;
  flag--;
  c();
 },200);
}
function c(){
 if(flag==0){
  console.log("after a and b:"+(aValue+bValue));
 }
}
a();
b();

我们设置了一个全局变量flag来监控方法a和方法b的完成情况。方法b通过设置一个200毫秒的定时器来模拟网络环境,最终会在b方法执行完成之后成功调用c方法。这样我们就实现了对方法a(),b(),c()的依赖调用。

(2)面向数据的控制

当上述方案在复杂场景下应用时,会出现如下问题:产品经过多个版本迭代,c方法依赖更多的方法,因此计数器flag需要不断的变化;产品迭代过程中更换了开发人员。当出现上述两种情况时,代码的逻辑将会变得混乱不堪,flag标记符是否能保持简明正确很大程度上受到了产品迭代的影响。因此我们提出面向数据的优化改进。

在真实的开发场景中,存在方法依赖的原因基本都是因为存在数据依赖,对于上面那个简单的示例:c方法依赖于a方法和b方法操作的结果,而不是依赖于flag是否为0。因此我们可以通过检查依赖方法是否已经完成了数据处理来代替检查标记符是否已经被置为0,在这个例子中也就是在c方法中检查aValue和bValue是否已经完成了赋值:

function c(){
 if(aValue!==undefined && bValue!==undefined){
  console.log("after a and b:"+(aValue+bValue));
 }
}

针对更加通用的场景,我们将上述代码修改为下:

var checkDependency={};
var aValue,bValue;
function a(){
 aValue=1;
 checkDependency.a=true;
 c();
}
function b(){
 setTimeout(function(){
  bValue=2;
  checkDependency.b=true;
  c();
 },200);
}
function c(){
 if(checkDependency.a && checkDependency.b){
  console.log("after a and b:"+(aValue+bValue));
 }
}
a();
b();

通过面向数据的检查方式,未来扩展时,我们仅需要在新增的方法中增加对checkDependency对象的修改,并且在c方法中检查相应属性的存在就能实现异步依赖方法的顺序执行。

四.ES6新增方法—Promise类

为了解决JavaScript中异步方法的复杂性,官方引入了一种统一的控制方式:

var bool=false;
/*
 * 新建一个Promise实例,向构造函数传入一个异步执行函数
 * 异步函数会接受两个参数,由Promise传入,对应then方法中传入的方法
 */
var promise=new Promise(function(resolve,reject){
 setTimeout(function(){
  if(bool){
   //根据执行情况相应调用resolve和reject
   resolve(bool);
  }else{
   reject(bool);
  }
 },200);
});
//通过then向Promise实例传入解决方法
promise.then(function resolve(result){
 console.log("success");
},function reject(result){
 console.log("failure");
});

上例代码展示了一个基础的Promise应用,也许实际场景中更加多见的是下面这种链式调用:

new Promise(function(res,rej){
 if(/*异步调用成功*/){
  res(data);
 }else{
  rej(error);
 }
}).then(function resolve(result){
 console.log("success");
},function reject(result){
 console.log("failure");
});

如果对Promise感兴趣的话,可以在网上寻找资料继续深入学习!

关于Promise的兼容性,通常web前端JavaScript代码中不会直接使用Promise(通过caniuse.com网站查询发现Android4.4不支持Promise)。如果特别想使用的,往往会在项目中附带一些补足兼容性的promise类库;而后端Node.js可以放心使用Promise类来管理异步逻辑。

详解js的异步编程技术的方法

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持三水点靠木。

Javascript 相关文章推荐
JQUERY CHECKBOX全选,取消全选,反选方法三
Aug 30 Javascript
为开发者准备的10款最好的jQuery日历插件
Feb 04 Javascript
火狐下input焦点无法重复获取问题的解决方法
Jun 16 Javascript
jquery简单图片切换显示效果实现方法
Jan 14 Javascript
js实现屏幕自适应局部代码分享
Jan 30 Javascript
jquery Easyui快速开发总结
Aug 20 Javascript
javascript 广告移动特效的实现代码
Jun 25 Javascript
fullPage.js和CSS3实现全屏滚动效果
May 05 Javascript
浅析为什么a="abc" 不等于 a=new String("abc")
Oct 25 Javascript
js实现控制文件拖拽并获取拖拽内容功能
Feb 17 Javascript
浅谈vue限制文本框输入数字的正确姿势
Sep 02 Javascript
JavaScript实现多文件下载方法解析
Aug 07 Javascript
原生JS实现简单放大镜效果
Feb 08 #Javascript
基于JavaScript实现本地图片预览
Feb 08 #Javascript
js 判断登录界面的账号密码是否为空
Feb 08 #Javascript
jQuery通过改变input的type属性实现密码显示隐藏切换功能
Feb 08 #Javascript
js获取地址栏中传递的参数(两种方法)
Feb 08 #Javascript
Bootstrap模态窗口源码解析
Feb 08 #Javascript
Bootstrap路径导航与分页学习使用
Feb 08 #Javascript
You might like
php date与gmdate的获取日期的区别
2010/02/08 PHP
PHP中全面阻止SQL注入式攻击分析小结
2012/01/30 PHP
PHP中几个可以提高运行效率的代码写法、技巧分享
2014/08/21 PHP
PHP伪造来源HTTP_REFERER的方法实例详解
2015/07/06 PHP
利用PHP如何实现Socket服务器
2015/09/23 PHP
WordPress中设置Post Type自定义文章类型的实例教程
2016/05/10 PHP
Laravel如何自定义command命令浅析
2019/03/23 PHP
Laravel5.5 动态切换多语言的操作方式
2019/10/25 PHP
php多进程并发编程防止出现僵尸进程的方法分析
2020/02/28 PHP
搭建PhpStorm+PhpStudy开发环境的超详细教程
2020/09/17 PHP
一个原生的用户等级的进度条
2010/07/03 Javascript
js确认删除对话框适用于a标签及submit
2014/07/10 Javascript
js插件设置innerHTML时在IE8下提示“未知运行时错误”解决方法
2015/04/25 Javascript
js文件中直接alert()中文出来的是乱码的解决方法
2016/11/01 Javascript
Base64(二进制)图片编码解析及在各种浏览器的兼容性处理
2017/02/09 Javascript
JavaScript实现翻页功能(附效果图)
2017/02/16 Javascript
jQuery插件zTree实现的基本树与节点获取操作示例
2017/03/08 Javascript
基于vue-cli vue-router搭建底部导航栏移动前端项目
2018/02/28 Javascript
React组件中的this的具体使用
2018/02/28 Javascript
JQuery属性操作与循环用法示例
2019/05/15 jQuery
js获取浏览器地址(获取第1个斜杠后的内容)
2019/09/03 Javascript
[01:10]3.19DOTA2发布会 三代刀塔人第一代
2014/03/25 DOTA
深度定制Python的Flask框架开发环境的一些技巧总结
2016/07/12 Python
详解Python中表达式i += x与i = i + x是否等价
2017/02/08 Python
python数据类型_字符串常用操作(详解)
2017/05/30 Python
学习python中matplotlib绘图设置坐标轴刻度、文本
2018/02/07 Python
详解Python 调用C# dll库最简方法
2019/06/20 Python
Python编写通讯录通过数据库存储实现模糊查询功能
2019/07/18 Python
基于Python的Jenkins的二次开发操作
2020/05/12 Python
NFL Game Pass欧洲:在线观看NFL比赛直播和点播,以高清质量播放
2018/08/30 全球购物
NYX Professional Makeup俄罗斯官网:世界知名的化妆品品牌
2019/12/26 全球购物
汇智创新科技发展有限公司
2015/12/06 面试题
欢迎领导标语
2014/06/27 职场文书
2014年公务员工作总结
2014/11/18 职场文书
2015入党个人自传范文
2015/06/26 职场文书
边城读书笔记
2015/06/29 职场文书