【javascript 进阶】异步调用

时间:2023-09-28 16:05:37

前言

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中的代码添加到执行队列中,通过例子说明吧

 setTimeout(function(){console.log("延迟一秒打印我")},1000);
console.log("打印出我");

当执行1行代码时,浏览器将里面的function(){console.log("延迟一秒打印我")}代码添加到了执行队列,并且延迟1秒之后执行,之后就到了第2行代码,打印了结果"打印出我",过了一秒之后打印出"延迟一秒打印我"。可以看出第1行代码执行了之后,并没有执行完,而是执行了第2行代码,1秒之后再执行setTimeout中的函数,就这样模拟异步函数调用,其实说白了并不是真正的异步函数调用,事实上所谓的异步只是一个假象,同样是运行在一个线程上。

假如现在在执行异步调用的时候,浏览器一直没有时间,那么就不用进行调用了,例子

 setTimeout(function(){console.log("延迟一秒打印我")},1000);
while(true){}//模拟一直很忙
console.log("打印出我");

上段代码啥都不会打印出来,因为代码function(){console.log("延迟一秒打印我")}插入之后,它要等1秒之后才能执行,但是浏览器一直都在忙,所以即使到了1秒钟之后,代码也不会被执行。

异步编程

一般来说,异步编程有四种方式,分别为回调函数方式,事件监听方式,发布-订阅方式和Promise方式,下面分别介绍这些方式。

回调函数

所谓回调函数,就是将函数作为参数传到需要回调的函数内部再执行,·例子,例如f1是很耗时的程序,f1执行完之后执行f2,如果是普通的这样

f1();
f2();

f2需要很长的时间才能执行,为了提高效率,我们使用回调函数的方式还模拟异步调用,这样f1(f2),怎样实现呢?其实就是利用setTimeout来实现

 function f1(func){
setTimeout(function(){
//f1代码
func();
},0);
}
function f2(){}
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

 var xmlhttp = new XMLHttpRequest();
xmlhttp.open("POST","url", true);
xmlhttp.onreadystatechange = function(){
if(xmlhttp.readyState == 4){
if(xmlhttp.status == 200){
console.log(xmlhttp.responseXML);
}
}
}
xmlhttp.send();

nodejs

 fs.readdir(".", function (err, filenames) {
var i;
for (i = 0; i < filenames.length; i++) {
console.log(filenames[i]);
}
});

事件监听

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)。

自己弄个订阅-发布模式,大家看看,勿喷啊

 (function(window,undefined){
var topics = {};
var queue = {
publish : function(topic,args){
if(!topics[topic]){
return;
}
var funcs = topics[topic],
len = funcs.length;
for(var i=0;i<len;i++){
funcs[i](args);
}
return this;
},
subscribe :function(topic,func){
if(!topics[topic]){
topics[topic] = [];
}
topics[topic].push(func);
}
}
window.AllenQueue = queue;
})(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。

例子如下

 function f1(){
    var dfd = $.Deferred();
    setTimeout(function () {
      // f1的任务代码
      dfd.resolve();
    }, 500);
    return dfd.promise;
}
f1().then(f2)

这个暂时就这样,以后我们会单独详细说说这个promise的。

小结

异步调用是javascript中一个重要的部分,我们还是要好好的掌握的。