详谈javascript异步编程


Posted in Javascript onFebruary 21, 2016

异步编程带来的问题在客户端Javascript中并不明显,但随着服务器端Javascript越来越广的被使用,大量的异步IO操作使得该问题变得明显。许多不同的方法都可以解决这个问题,本文讨论了一些方法,但并不深入。大家需要根据自己的情况选择一个适于自己的方法。

本文为大家详细介绍js中的异步编程,具体内容如下

一 关于事件的异步

事件是JavaScript中最重要的一个特征,nodejs就是利用js这一异步而设计出来的。所以这里讲一下事件机制。

在一个js文件中,如果要运行某一个函数,有2中手段,一个就是直接调用,比如foo(),第二就是利用事件来触发,这中函数也叫回调函数,比如传递给setTimeout函数和onready属性。

1.setTimeout函数中的事件异步
setTimeout本质上也是一种异步事件,当延迟时间到的时候触发该事件,但是有的有的时候(其实也是大部分时候)都不会按照给定的延迟时间执行,先看下面的代码

var start = new Date();
  setTimeout(function() {
   console.log('settimeout1:',new Date()-start);
  }, 500);
  while (new Date() - start < 1000) {
   console.log('in while');
  }
  document.getElementById('test').addEventListener('click', function(){
   console.log('test:',new Date()-start);
  }, false)
  for(var i=0;i<10000;i++){
   console.log('in for');
  }
  setTimeout(function(){
   console.log('settimeout2: ',new Date()-start);
  },1000);
  /* 10214
  in while
  index.jsp (第 19 行)
  10000
  in for
  index.jsp (第 25 行)
  settimeout1: 2263
  index.jsp (第 16 行)
  settimeout2: 3239
  index.jsp (第 28 行)
  test: 10006
  index.jsp (第 22 行)
  test: 28175
  index.jsp (第 22 行)
  test: 28791
  index.jsp (第 22 行)
  test: 28966
  index.jsp (第 22 行) */

如果按照正常的理解,延迟函数应该在500毫秒之后打断while循环,而事实上并没有,并且,我在while循环和for循环期间点击div时候并没有立即输出test,给出的解释就是:

a)事件队列。调用setTimeout函数的时候,会把传入它的回调函数加入到事件队列中去(事件已经初始化并且在内存了),然后继续执行后面的代码,直到再也没有代码可以运行(没有正常的运行流了,不包括事件函数等异步的内容),就会从事件队列里面pop出一个合适的事件来运行。

b)js是单线程的,事件处理器在线程空闲之前是不会运行的。

2 普通事件的异步和setTimeout类似
二 promise对象和deferred对象

1. promise
promise是一种解决ajax等异步编程回调函数嵌套太多导致代码晦涩难懂的解决方案,特别是在nodejs中,异步无处不在。不同的框架对promise的实现,一下是jquery中的promise的API。

这里不讲promise的实现原理,关于原理在另外的篇幅中介绍。

传统的ajax异步编程是这么写的(jquery1.5之前):

$.get('url', function(){
 $.get('url1', function(){
  $.get('url2', function(){

  }, 'json');
 }, 'json');
}, 'json');

 这么写代码给开发和维护带来了极大的困难,好在jquery1.5以后引入了promise,就可以这么写了:

$.ajax( "example.php" )
.done(function() { alert("success"); })
.fail(function() { alert("error"); })
.always(function() { alert("complete"); });

 现在看上去就明显简单多了。

2.deferred对象

var nanowrimoing = $.Deferred();
var wordGoal = 5000;
nanowrimoing.progress(function(wordCount) {
var percentComplete = Math.floor(wordCount / wordGoal * 100);
$('#indicator').text(percentComplete + '% complete');
});
nanowrimoing.done(function(){
$('#indicator').text('Good job!');
});

三.worker对象和多线程

四.异步脚本加载

1.传统脚本在页面中的位置
脚本分为两大类:阻塞式和非阻塞式。这里的阻塞是指加载阻塞而不是运行阻塞。

<!DOCTYPE html>
<html>
<head>
<script src="headScript"></script>
<script defer src="deferredScript"></script>
</head>
<body>
 <script async defer src="chatWidget"></script>
 <script async defer src="asyncScript"></script>
</body>
</html>

上面这部分代码是比较标准的关于脚本在一个页面中的位置,1.其中传统的未加任何修饰的headScript是阻塞式的脚本,由于浏览器从上到下解释执行JavaScript,所以这部分脚本文件在一开始就会被执行,并且在执行完之前是DOM是不会渲染的,但是head标签里面的css会加载。2.有defer属性的脚本会在DOM渲染的同时进行加载,但是会在DOM渲染完毕之后才开始执行,不幸的是,不是所有的浏览器都支持defer属性,所以才会有了jquery(function)这个东西。3.同时带有async属性和defer属性时候,defer会覆盖async,但是单独有async的时候,脚本会在DOM渲染的时候加载并且运行。

2.可编程的脚本加载
如果不是一开始就在页面种引入js文件,而是通过用户交互来实现动态的加载js脚本,可以通过编程方式加入。

浏览器获取服务器脚本有2个方法,ajax获取并且通过eval函数执行,另外一个就是在DOM中插入<script>标签,一般用第二种方法,因为浏览器帮助我们生成HTTP请求以及eval会泄露作用域。

var head = document.getElementsByTagName('head')[0];
var script = document.createElement('script');
script.src = '/js/feature.js';
head.appendChild(script);
script.onload = function() {
// 现在可以调用脚本里定义的函数了
}

以上就是本文的全部内容,希望对大家学习js异步编程有所帮助。

Javascript 相关文章推荐
13个绚丽的Jquery 界面设计网站推荐
Sep 28 Javascript
Javascript在IE下设置innerHTML时出现未知的运行时错误的解决方法
Jan 12 Javascript
THREE.JS入门教程(2)着色器-上
Jan 24 Javascript
javascript history对象(历史记录)使用方法(实现浏览器前进后退)
Jan 07 Javascript
js换图片效果可进行定时操作
Jun 09 Javascript
node.js [superAgent] 请求使用示例
Mar 13 Javascript
js检测离开或刷新页面时表单数据是否更改的方法
Aug 02 Javascript
基于vue.js路由参数的实例讲解——简单易懂
Sep 07 Javascript
浅谈react前后端同构渲染
Sep 20 Javascript
通过js控制时间,一秒一秒自己动的实例
Oct 25 Javascript
node.js中 mysql 增删改查操作及async,await处理实例分析
Feb 11 Javascript
JavaScript适配器模式原理与用法实例详解
Mar 09 Javascript
浅谈javascript的call()、apply()、bind()的用法
Feb 21 #Javascript
EasyUI闪屏EasyUI页面加载提示(原理+代码+效果图)
Feb 21 #Javascript
javascript+HTML5自定义元素播放焦点图动画
Feb 21 #Javascript
JavaScript编程学习技巧汇总
Feb 21 #Javascript
Node.js开发者必须了解的4个JS要点
Feb 21 #Javascript
JSON简介以及用法汇总
Feb 21 #Javascript
javascript实现计时器的简单方法
Feb 21 #Javascript
You might like
YII路径的用法总结
2014/07/09 PHP
PHP中预定义的6种接口介绍
2015/05/12 PHP
PHPMailer使用QQ邮箱实现邮件发送功能
2017/08/18 PHP
如何在centos8自定义目录安装php7.3
2019/11/28 PHP
PHP随机生成中文段落示例【测试网站内容时使用】
2020/04/26 PHP
php屏蔽错误及提示的方法
2020/05/10 PHP
解读IE和firefox下JScript和HREF的执行顺序
2008/01/12 Javascript
javascript实现文本域写入字符时限定字数
2014/02/12 Javascript
js jquery获取当前元素的兄弟级 上一个 下一个元素
2015/09/01 Javascript
JavaScript学习笔记之ES6数组方法
2016/03/25 Javascript
原生JS实现轮播效果+学前端的感受(防止走火入魔)
2016/08/21 Javascript
模板视图和AngularJS之间冲突的解决方法
2016/11/22 Javascript
JS 调用微信扫一扫功能
2016/12/22 Javascript
详解Vue的常用指令v-if, v-for, v-show,v-else, v-bind, v-on
2018/10/12 Javascript
一个Java程序猿眼中的前后端分离以及Vue.js入门(推荐)
2019/04/19 Javascript
JavaScript实现的开关灯泡点击切换特效示例
2019/07/08 Javascript
javascript面向对象创建对象的方式小结
2019/07/29 Javascript
JavaScript 预解析的4种实现方法解析
2019/09/03 Javascript
微信小程序通过websocket实时语音识别的实现代码
2020/08/19 Javascript
使用Python生成随机密码的示例分享
2016/02/18 Python
Python实现求两个csv文件交集的方法
2017/09/06 Python
PyCharm 常用快捷键和设置方法
2017/12/20 Python
Python操作配置文件ini的三种方法讲解
2019/02/22 Python
Pycharm小白级简单使用教程
2020/01/08 Python
Python操作dict时避免出现KeyError的几种解决方法
2020/09/20 Python
python中remove函数的踩坑记录
2021/01/04 Python
定制iPhone和Macbook保护壳:Slick Case
2018/11/21 全球购物
大学生四个方面的自我评价
2013/09/19 职场文书
小学生寒假家长评语
2014/04/16 职场文书
广告设计专业毕业生自我鉴定
2014/09/27 职场文书
教师个人查摆剖析材料
2014/10/14 职场文书
2015年公民道德宣传日活动总结
2015/03/23 职场文书
教师节晚会主持词
2015/06/30 职场文书
秋季运动会加油词
2015/07/18 职场文书
党员学习型组织心得体会
2019/06/21 职场文书
css如何把元素固定在容器底部的四种方式
2022/06/16 HTML / CSS