【javascript 进阶】异步调用
前言
javascript的中的异步是很重要的概念,特别是ajax的提出,给整个web带来了很大的影响,今天就介绍下javascript的异步编程。
同步与异步
何为同步?何为异步呢?
同步:说白了就是程序一步一步从下向下执行,没有什么别的代码的跳动,就是按序执行,和在景区里女生上厕所是排队是一样的(每次女厕都是有好多人在排队)。可以看成是一个单线程问题。
异步:异步就是程序可以跳着执行,开始执行一段程序之后不用等返回结果就执行其他的代码,等结果返回之后在对结果进行处理,也就是可以在有限的时间内办好几件事情,提高效率,异步一般情况是多个线程问题。举个例子,还是上厕所的例子,A说需要5分钟搞定,B就可以5分钟之后过来,B可以在这5分钟干点别的事情。
所以说异步可以提高程序的执行效率,所以异步编程具有一定的好处,但是编写异步程序却不是那么容易的。
上面是我的理解,下面是摘自别人的文章:
"同步模式"就是上一段的模式,后一个任务等待前一个任务结束,然后再执行,程序的执行顺序与任务的排列顺序是一致的、同步的;"异步模式"则完全不同,每一个任务有一个或多个回调函数(callback),前一个任务结束后,不是执行后一个任务,而是执行回调函数,后一个任务则是不等前一个任务结束就执行,所以程序的执行顺序与任务的排列顺序是不一致的、异步的。
浏览器机制
我们都知道了,浏览器是单线程解析javascript的,也就是说,我们的浏览器原理是是不能搞成异步的,因为就一个线程嘛,如何搞成异步的呢?这就需要模拟异步来实现了,主要需要setTimeout函数。
setTimeout和setInterval
setTImeout是指延迟指定的毫秒数后调用函数或计算表达式。看个例子
setTimeout(function(){alert("延迟一秒弹出")},1000);
setInterval方法可按照指定的周期(以毫秒计)来调用函数或计算表达式,就是一定时间之后重新执行一次,例子
setInterval(function(){alert("一秒弹一次")},1000);
对应的取消方法是clearTimeout和clearInterval,用法很简单。
用上面的两个方法模拟所谓的“异步调用”事实上是通过将代码段插入到代码的执行队列中实现的,因为javascript是单线程执行的,程序按序执行下来时,等浏览器有时间了就等一定的时间将setTimeout中的代码添加到执行队列中,通过例子说明吧
1 setTimeout(function(){console.log("延迟一秒打印我")},1000); 2 console.log("打印出我");
当执行1行代码时,浏览器将里面的function(){console.log("延迟一秒打印我")}代码添加到了执行队列,并且延迟1秒之后执行,之后就到了第2行代码,打印了结果"打印出我",过了一秒之后打印出"延迟一秒打印我"。可以看出第1行代码执行了之后,并没有执行完,而是执行了第2行代码,1秒之后再执行setTimeout中的函数,就这样模拟异步函数调用,其实说白了并不是真正的异步函数调用,事实上所谓的异步只是一个假象,同样是运行在一个线程上。
假如现在在执行异步调用的时候,浏览器一直没有时间,那么就不用进行调用了,例子
1 setTimeout(function(){console.log("延迟一秒打印我")},1000); 2 while(true){}//模拟一直很忙 3 console.log("打印出我");
上段代码啥都不会打印出来,因为代码function(){console.log("延迟一秒打印我")}插入之后,它要等1秒之后才能执行,但是浏览器一直都在忙,所以即使到了1秒钟之后,代码也不会被执行。
异步编程
一般来说,异步编程有四种方式,分别为回调函数方式,事件监听方式,发布-订阅方式和Promise方式,下面分别介绍这些方式。
回调函数
所谓回调函数,就是将函数作为参数传到需要回调的函数内部再执行,·例子,例如f1是很耗时的程序,f1执行完之后执行f2,如果是普通的这样
f1();
f2();
f2需要很长的时间才能执行,为了提高效率,我们使用回调函数的方式还模拟异步调用,这样f1(f2),怎样实现呢?其实就是利用setTimeout来实现
1 function f1(func){ 2 setTimeout(function(){ 3 //f1代码 4 func(); 5 },0); 6 } 7 function f2(){} 8 f1(f2);
自己弄个例子吧
function f1(func){ setTimeout(function(){ for(var i=0;i<10000;i++){ console.log(i); } func(); },0); } function f2(){ console.log("f2"); } f1(f2); console.log("程序末尾");
结果:程序末尾 1 2 ...10000 f2
程序f1执行结束之后执行f2,f1执行的时候不影响后面程序的执行,所以先显示“程序末尾”,然后是1.2.3.。。。。,最后是回调函数f2的结果。
ajax和nodejs主要就是通过回调函数这样方式实现的异步调用,简单介绍一下
ajax
1 var xmlhttp = new XMLHttpRequest(); 2 xmlhttp.open("POST","url", true); 3 xmlhttp.onreadystatechange = function(){ 4 if(xmlhttp.readyState == 4){ 5 if(xmlhttp.status == 200){ 6 console.log(xmlhttp.responseXML); 7 } 8 } 9 } 10 xmlhttp.send();
nodejs
1 fs.readdir(".", function (err, filenames) { 2 var i; 3 for (i = 0; i < filenames.length; i++) { 4 console.log(filenames[i]); 5 } 6 });
事件监听
javascript的事件驱动模式是重要的概念,代码的执行和顺序没有关系,而是与事件的何时发生有关系。javascript原生的绑定事件就是这个概念,例如
dom.onclick = function(){ console.log("click"); }
jquery就是类似这样的,都是事件绑定,这是演示,
function f2(){} f1.on("complete",f2); f1.trigger("complete");
订阅-发布
简单说:两种角色,订阅者和发布者,订阅者可以订阅事件,发布者发布事件消息,发布者发布消息只能通知到订阅改消息的订阅者,未订阅的不同接收到消息。专业解释:存在一个"信号中心",某个任务执行完成,就向信号中心"发布"(publish)一个信号,其他任务可以向信号中心"订阅"(subscribe)这个信号,从而知道什么时候自己可以开始执行。这就叫做"发布/订阅模式"(publish-subscribe pattern),又称"观察者模式"(observer pattern)。
自己弄个订阅-发布模式,大家看看,勿喷啊
1 (function(window,undefined){ 2 var topics = {}; 3 var queue = { 4 publish : function(topic,args){ 5 if(!topics[topic]){ 6 return; 7 } 8 var funcs = topics[topic], 9 len = funcs.length; 10 for(var i=0;i<len;i++){ 11 funcs[i](args); 12 } 13 return this; 14 }, 15 subscribe :function(topic,func){ 16 if(!topics[topic]){ 17 topics[topic] = []; 18 } 19 topics[topic].push(func); 20 } 21 } 22 window.AllenQueue = queue; 23 })(window);
用法:
AllenQueue.subscribe("done",function(){console.log("done")}); AllenQueue.publish("done")
其实我觉得时间监听就是订阅发布模式,这两个应该是一样的,事件机制就是订阅发布的实例。
Promise
promise对象是CommonJS工作组提供的一种规范,用于异步编程的统一接口。promise对象通常实现一种then的方法,用来在注册状态发生改变时作为对应的回调函数。promise模式在任何时刻都处于以下三种状态之一:未完成(unfulfilled)、已完成(resolved)和拒绝(rejected)。以CommonJS Promise/A 标准为例,promise对象上的then方法负责添加针对已完成和拒绝状态下的处理函数。then方法会返回另一个promise对象,以便于形成promise管道,这种返回promise对象的方式能够支持开发人员把异步操作串联起来,如then(resolvedHandler, rejectedHandler)。resolvedHandler 回调函数在promise对象进入完成状态时会触发,并传递结果;rejectedHandler函数会在拒绝状态下调用。
Jquery在1.5的版本中引入了一个新的概念叫Deferred,就是CommonJS promise A标准的一种衍生。可以在jQuery中创建$.Deferref的对象。同时也对发送ajax请求以及数据类型有了新的修改,参考JQuery API。
例子如下
1 function f1(){ 2 var dfd = $.Deferred(); 3 setTimeout(function () { 4 // f1的任务代码 5 dfd.resolve(); 6 }, 500); 7 return dfd.promise; 8 } 9 f1().then(f2)
这个暂时就这样,以后我们会单独详细说说这个promise的。
小结
异步调用是javascript中一个重要的部分,我们还是要好好的掌握的。