JavaScript异步编程的方法

异步编程

在浏览器端,异步编程非常重要,耗时很长的操作都应该异步执行,避免浏览器失去响应。最常见的例子就是通过AJAX向服务器发送异步请求。

异步编程有很多种方法

1、回调函数

比如有两个函数
f1();
f2();//f2依赖于f1的执行状态
如果f1耗时很长,它会阻塞后面程序的运行
我们利用setTimeout来改写f1,因为setTimeout是异步的

function f1(callback){
    setTimeout(function(){
        //f1的代码,耗时很长,这里是又开启了一个线程,
        callback();//f1完后执行callback()
    },1000)
}

f1(f2);//这段代码不会阻塞后面的程序运行,f2依赖于f1的执行状态

采用这种方式,我们把同步操作变成了异步操作,f1不会堵塞程序运行,相当于先执行程序的主要逻辑,将耗时的操作推迟执行。
这种方法很简单,也容易理解,但是不利于代码的阅读和维护,各个部分之间高度耦合,而且每个任务只能指定一个回调函数

2、事件监听
另一种思路是采用事件驱动模式。任务的执行不取决于代码的顺序,而取决于某个事件是否发生。

f1.on('done',f2);//为f1绑定一个事件(f2订阅事件)
function f1(){
    setTimeout(function(){
        //f1的代码
        f1.trigger('done');//f1触发这个事件(f1发布事件)
    },1000)
}

这种方法可以绑定多个事件,每个事件可以指定多个回调函数,而且可以"去耦合"有利于实现模块化。
缺点是整个程序都要变成事件驱动型,运行流程会变得很不清晰。

3.发布订阅模式
又称观察者模式,它广泛的应用于异步编程中,是一种代替回调函数的方案。
上面讲到的事件,可以理解成“信号”
假定,存在一个"信号中心",某个任务执行完成,就向信号中心"发布"一个信号,其他任务可以向信号中心"订阅"这个信号,从而知道什么时候自己可以开始执行。
这就叫做"发布/订阅模式",又称"观察者模式"
发布订阅者模式可以去除对象之间的耦合,有利于实现模块化,而且我们可以通过查看"消息中心",了解存在多少信号、每个信号有多少订阅者,从而监控程序的运行。

这个模式有多种实现,下面采用的是jQuery的一个插件。

//f2向"信号中心"jQuery订阅"done"信号
jQuery.subscribe("done", f2);
function f1(){
    setTimeout(function () {
      // f1的任务代码
      jQuery.publish("done");//f1执行完成后,向"信号中心"jQuery发布"done"信号,从而引发f2的执行。
    }, 1000);
  }

4.Promise对象
Promises对象是CommonJS工作组提出的一种规范,目的是为异步编程提供统一接口,使得控制异步操作更加的容易
ES6将Promsie纳入了规范,并原生提供了Promise对象
简单说,它的思想是,每一个异步任务返回一个Promise对象,该对象有一个then方法,允许指定回调函数。比如,f1的回调函数f2,可以写成:
f1().then(f2);
promise优点在于避免层层嵌套的回调函数,回调函数变成了链式写法,程序的流程可以看得很清楚,并且提供了统一的接口,使得控制异步操作更加的容易。
而且,它还有一个前面三种方法都没有的好处:如果一个任务已经完成,再添加回调函数,该回调函数会立即执行。所以,你不用担心是否错过了某个事件或信号。
这种方法的缺点就是编写和理解,都相对比较难。

例子:我们用promise对象实现AJAX操作

var getJSON=function(url){
    var promise=new Promise(function(resolve,reject){
        //AJAX操作
        $.ajax({
            url:url,
            type:'GET',
            dataType:'json',
            success:function(data,status,xhr){
                resolve(data);
            },
            error:function(xhr,textStatus){
                reject(textStatus);
            }
        })
    })
    return promise;
}
getJSON('/post.json')
    //任务Resolve状态的回调函数,返回是这个promise对象
    .then(function(value) {
        console.log(value);
    })
    //任务Rejected状态的回调函数
    .catch(function(error){
        console.log(error);
    })

采用链式的then方法可以指定按照次序调用的回调函数
如果前一个回调函数返回的是另外一个promise对象,那么后一个回调函数就会等待该promise对象状态发生变化再调用

getJSON("url1")
    .then(function(value){
         return getJSON(value.url2);//返回另外一个promise对象
     })
     //另外一个promise对象Resolve状态的回调函数
    .then(function(value){
        console.log(value)
    })
原文地址:https://www.cnblogs.com/t1amo/p/6851979.html