详谈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 相关文章推荐
地址栏上的一段语句,改变页面的风格。(教程)
Apr 02 Javascript
Jquery index()方法 获取相应元素索引值
Oct 12 Javascript
jquery scroll()区分横向纵向滚动条的方法
Apr 04 Javascript
巧用replace将文字表情替换为图片
Apr 17 Javascript
基于jQuery+PHP+Mysql实现在线拍照和在线浏览照片
Sep 06 Javascript
jquery实现表格中点击相应行变色功能效果【实例代码】
May 09 Javascript
温故知新——JavaScript中的字符串连接问题最全总结(推荐)
Aug 21 Javascript
javascript算法之二叉搜索树的示例代码
Sep 12 Javascript
ES6 javascript的异步操作实例详解
Oct 30 Javascript
vue新vue-cli3环境配置和模拟json数据的实例
Sep 19 Javascript
Vue 列表上下过渡效果的实例代码
Jun 25 Javascript
JS闭包原理及其使用场景解析
Dec 03 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
PHP操作mysql函数详解,mysql和php交互函数
2011/05/19 PHP
PHP合并两个数组的两种方式的异同
2012/09/14 PHP
淘宝ip地址查询类分享(利用淘宝ip库)
2014/01/07 PHP
php+ajax实时输入自动搜索匹配的方法
2014/12/26 PHP
php xhprof使用实例详解
2019/04/15 PHP
PHP如何使用cURL实现Get和Post请求
2020/07/11 PHP
基于jquery的多功能软键盘插件
2012/07/25 Javascript
url参数中有+、空格、=、%、&amp;、#等特殊符号的问题解决
2013/05/15 Javascript
Jquery加载时从后台读取数据绑定到dropdownList实例
2013/06/09 Javascript
JQuery的ready函数与JS的onload的区别详解
2013/11/21 Javascript
javascript实现的一个随机点名功能
2014/08/26 Javascript
JavaScript中switch判断容易犯错的一个细节
2014/08/27 Javascript
基于jQuery仿淘宝产品图片放大镜特效
2020/10/19 Javascript
jQuery插件实现图片轮播特效
2016/06/16 Javascript
Vue2.0 实现移动端图片上传功能
2018/05/30 Javascript
ES10 特性的完整指南小结
2019/03/04 Javascript
vue下拉刷新组件的开发及slot的使用详解
2020/12/23 Vue.js
跟老齐学Python之正规地说一句话
2014/09/28 Python
情人节快乐! python绘制漂亮玫瑰
2020/08/18 Python
Python 存储字符串时节省空间的方法
2019/04/23 Python
基于python3的socket聊天编程
2020/02/17 Python
Keras之fit_generator与train_on_batch用法
2020/06/17 Python
Python读取图像并显示灰度图的实现
2020/12/01 Python
JavaScript+Canvas实现自定义画板的示例代码
2019/05/13 HTML / CSS
美国奢侈品购物平台:Orchard Mile
2018/05/02 全球购物
解释DataSet(ds) 和 ds as DataSet 的含义
2014/07/27 面试题
大学应届毕业生个人求职信
2013/09/23 职场文书
自我评价格式
2014/01/06 职场文书
2016年八一建军节活动总结
2016/04/05 职场文书
开发一个封装iframe的vue组件
2021/03/29 Vue.js
写好Python代码的几条重要技巧
2021/05/21 Python
Pytorch中Softmax与LogSigmoid的对比分析
2021/06/05 Python
Oracle 临时表空间SQL语句的实现
2021/09/25 Oracle
Java十分钟精通进阶适配器模式
2022/04/06 Java/Android
Go语言 详解net的tcp服务
2022/04/14 Golang