ZhangYang's Blog

JavaScript实现异步

JavaScript的执行环境是单线程,只能一次完成一件任务

坏处: 浏览器无响应、JavaScript代码死循环

Javascript语言将任务的执行模式分成两种:同步和异步

解决方法:异步

异步:每一个任务有一个或多个回调函数,前一个任务结束后,不是执行后一个任务,而是执行回调函数,后一个任务则是不等前一个任务结束就执行,所以程序的执行顺序与任务的排列顺序是不一致的、异步的

回调函数

普通函数f1和f2,后者等待前者的执行结果

1
2
f1()
f2()

改写f1,把f2写成f1的回调函数,两个函数异步执行

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

优点:简单、容易理解和部署,缺点:不利于代码的阅读和维护

事件监听

任务的执行不取决于代码的顺序,而取决于某个事件是否发生

为f1绑定一个事件,当f1发生done事件,就执行f2

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

f1.trigger(‘done’)表示,执行完成后,立即触发done事件,从而开始执行f2

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

优点:比较容易理解,可以绑定多个事件,有利于实现模块化,缺点:整个程序都要变成事件驱动型,运行流程会变得很不清晰

发布/订阅

发布一个信号,其他任务向信号中心订阅这个信号,从而知道什么时候自己可以开始执行。这就叫做发布/订阅模式

f2向”信号中心”jQuery订阅”done”信号

1
jQuery.subscribe("done", f2);

jQuery.publish(“done”)的意思是,f1执行完成后,向”信号中心”jQuery发布”done”信号,从而引发f2的执行

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

f2完成执行后,也可以取消订阅

1
jQuery.unsubscribe("done", f2);

Promises对象

每一个异步任务返回一个Promise对象,该对象有一个then方法,允许指定回调函数

f1的回调函数f2

1
f1().then(f2);

f1要进行如下改写

1
2
3
4
5
6
7
8
function f1() {
var dfd = $.Deferred();
setTimeout(function () {
// f1的任务代码
dfd.resolve();
}, 500);
return dfd.promise;
}

指定多个回调函数

1
f1().then(f2).then(f3);

指定发生错误时的回调函数

1
f1().then(f2).fail(f3);

Ajax

同步的写法

taskC 一定要等 taskB 执行完了才能执行,这就是同步

1
2
3
4
5
6
7
8
9
10
11
12
13
14
function taskB(){
var response = $.ajax({
url:"/data.json",
async: false // 注意这里 async 为 false,表示是同步
})
return response // 十秒钟后,返回 response
}
taskA()
taskB()
taskC()
执行顺序
A -> B -> AJAX 请求 -> C ---------------------------

异步的写法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
function taskB(){
var result = $.ajax({
url:"/data.json",
async: true // 异步
})
return result // 一定要注意,现在的 result 不是上面的 response
}
taskA()
taskB()
taskC()
执行顺序
A -> B -> C ---------------------------------------
-> AJAX 请求 --------------------------------