这篇文章讲一讲JavaScript的异步处理。其实在JavaScript核心语法的时候,我已经讲过了异步处理,但是那时候仅仅是我知道怎么用,大概是什么样的一个东西而已,你要让我说出本质,我还是不懂,今天我就把异步处理给说清楚。
异步操作的模式用的最多的就是2种:回调函数和promise对象。
回调函数
回调函数是异步操作最基本的方法。
下面是两个函数f1
和f2
,编程的意图是f2
必须等到f1
执行完成,才能执行。
function f1() {
// ...
}
function f2() {
// ...
}
f1();
f2();
上面代码的问题在于,如果f1
是异步操作,f2
会立即执行,不会等到f1
结束再执行。
这时,可以考虑改写f1
,把f2
写成f1
的回调函数。
function f1(callback) {
console.log("Inside f1");
callback(); // 调用传入的回调函数
}
function f2() {
console.log("Inside f2");
}
f1(f2);
运行结果:
Inside f1
Inside f2
回调函数的优点是简单、容易理解和实现,缺点是不利于代码的阅读和维护,各个部分之间高度耦合),使得程序结构混乱、流程难以追踪(尤其是多个回调函数嵌套的情况)。
Promise 对象
首先,Promise 是一个对象,也是一个构造函数。
function f1(resolve, reject) {
// 异步代码...
}
var p1 = new Promise(f1);
上面代码中,Promise
构造函数接受一个回调函数f1
作为参数,f1
里面是异步操作的代码。然后,返回的p1
就是一个 Promise 实例。
Promise 的设计思想是,所有异步任务都返回一个 Promise 实例。Promise 实例有一个then
方法,用来指定下一步的回调函数。
var p1 = new Promise(f1);
p1.then(f2);
上面代码中,f1
的异步操作执行完成,就会执行f2
。
传统的写法可能需要把f2
作为回调函数传入f1
,比如写成f1(f2)
,异步操作完成后,在f1
内部调用f2
。Promise 使得f1
和f2
变成了链式写法。不仅改善了可读性,而且对于多层嵌套的回调函数尤其方便。
// 传统写法
step1(function (value1) {
step2(value1, function(value2) {
step3(value2, function(value3) {
step4(value3, function(value4) {
// ...
});
});
});
});
// Promise 的写法
(new Promise(step1))
.then(step2)
.then(step3)
.then(step4);
从上面代码可以看到,采用 Promises 以后,程序流程变得非常清楚,十分易读。Promise 的用法,简单说就是一句话:使用then
方法添加回调函数。
总的来说,传统的回调函数写法使得代码混成一团,变得横向发展而不是向下发展。Promise 就是解决这个问题,使得异步流程可以写成同步流程。