- f1(f2);
采用这种方式,我们把同步操作变成了异步操作,f1 不会堵塞程序运行,相当于先执行程序的主要逻辑,将耗时的操作推迟执行。
回调函数的优点是简单、容易理解和部署,缺点是不利于代码的阅读和维护,各个部分之间高度耦合(Coupling),流程会很混乱,而且每个任务只能指定一个回调函数。
二、事件监听
另一种思路是采用事件驱动模式。任务的执行不取决于代码的顺序,而取决于某个事件是否发生。
还是以 f1 和 f2 为例。首先,为 f1 绑定一个事件(这里采用的 jQuery 的写法)。
上面这行代码的意思是,当 f1 发生 done 事件,就执行 f2。然后,对 f1 进行改写:
- f1.on('done', f2);
- function f1(){
setTimeout(function () {
// f1 的任务代码
f1.trigger('done');
}, 1000);
}
f1.trigger('done') 表示,执行完成后,立即触发 done 事件,从而开始执行 f2。这种方法的优点是比较容易理解,可以绑定多个事件,每个事件可以指定多个回调函数,而且可以 "去耦合"(Decoupling),有利于实现模块化。缺点是整个程序都要变成事件驱动型,运行流程会变得很不清晰。
三、发布 / 订阅
上一节的 "事件",完全可以理解成 "信号"。
我们假定,存在一个 "信号中心",某个任务执行完成,就向信号中心 "发布"(publish)一个信号,其他任务可以向信号中心 "订阅"(subscribe)这个信号,从而知道什么时候自己可以开始执行。这就叫做 "发布 / 订阅模式"(publish-subscribe pattern),又称 "观察者模式"(observer pattern)。
这个模式有多种实现,下面采用的是 Ben Alman 的 Tiny Pub/Sub,这是 jQuery 的一个插件。
首先,f2 向 "信号中心"jQuery 订阅 "done" 信号。
然后,f1 进行如下改写:
- jQuery.subscribe("done", f2);
- function f1(){
setTimeout(function () {
// f1 的任务代码
jQuery.publish("done");
}, 1000);
}
jQuery.publish("done") 的意思是,f1 执行完成后,向 "信号中心"jQuery 发布 "done" 信号,从而引发 f2 的执行。
此外,f2 完成执行后,也可以取消订阅(unsubscribe)。
- jQuery.unsubscribe("done", f2);
这种方法的性质与 "事件监听" 类似,但是明显优于后者。因为我们可以通过查看 "消息中心",了解存在多少信号、每个信号有多少订阅者,从而监控程序的运行。
四、Promises 对象
Promises 对象是 CommonJS 工作组提出的一种规范,目的是为异步编程提供统一接口。
简单说,它的思想是,每一个异步任务返回一个 Promise 对象,该对象有一个 then 方法,允许指定回调函数。比如,f1 的回调函数 f2, 可以写成:
- f1().then(f2);
f1 要进行如下改写(这里使用的是 jQuery 的实现):
- function f1(){
var dfd = $.Deferred();
setTimeout(function () {
// f1 的任务代码
dfd.resolve();
}, 500);
return dfd.promise;
}
来源: http://www.phperz.com/article/17/0507/274729.html