Javascript 异步编程

背景

我们都知道,Javascript 语言的执行环境是“单线程”的。单线程在程序执行时,一次只能完成一个任务。如果有多个任务,就必须排队,前面一个任务完成,在执行后面一个任务,以此类推。

这种模式的好处是实现起来比较简单,执行环境相对单纯;坏处是只要有一个任务耗时很长,后面的任务都必须排队等着,会拖延整个程序的执行。常见的浏览器无响应(假死),往往就是因为某一段Javascript代码长时间运行(比如死循环),导致整个页面卡在这个地方,其他任务无法执行。

基于JavaScript的单线程语言执行环境及其不足,Javascript语言将任务的执行模式分成两种:同步(Synchronous)和异步(Asynchronous)。

同步,即任务一步一步执行,当前任务执行完毕后才能继续执行后续任务;异步则是后续任务不需等待前一个任务结束也可执行,当前任务执行结束后可以通过状态、通知或回调来通知调用者。

异步编程实现

回调函数

回调函数是异步编程最基本的方法。

假定有两个函数 f1 和 f2,f2 依赖 f1 的执行结果。

1
2
3
f1();

f2();

如果 f1 是一个很耗时的任务,这时我就可以考虑把 f2 写出 f1 的回调函数。

1
2
3
4
5
6
7
8
function f1(callback) {
setTimeout(function () {
   // f1的任务代码
   callback();
}, 1000);
}

f1(f2);

采用回调函数,我们就把同步操作变成了异步操作,这样 f1 就不会堵塞主程序运行,相当于先执行主程序逻辑,将耗时的操作推迟执行。

回调函数的优点是简单、容易理解和部署,缺点是不利于代码的阅读和维护,各个部分之间高度耦合(Coupling),流程会很混乱,而且每个任务只能指定一个回调函数。

事件监听

采用事件驱动模式,任务的执行不取决于代码的顺序,而取决于某个事件是否发生。

假定有两个函数 f1 和 f2,f1绑定一个 done 事件(这里采用的jQuery的写法

1
f1.on('done', f2);

对 f1 进行改写:

1
2
3
4
5
6
function f1(){
setTimeout(function () {
// f1的任务代码
f1.trigger('done');
}, 1000);
}

上述代码表示,执行完 f1 任务,立即触发 done 事件,当 f1 发生 done 事件,就执行 f2。

事件监听的优点是可以绑定多个事件,每个事件可以指定多个回调函数,而且可以”去耦合”(Decoupling),有利于实现模块化。缺点是整个程序都要变成事件驱动型,运行流程会变得很不清晰。

您的支持将鼓励我继续创作!