JavaScript 中使用 Promise 处理异步任务
简介
过去,这种行为通过回调函数来处理,但是随着时间的推移,这变得难以维护,并且随着需要执行的验证和派生过程越来越多,代码缩进也变得越来越有问题。这导致了 JavaScript 中 Promise 的引入。
JavaScript Promise 是在 ECMAScript 6 中引入的,为该语言带来了更简洁的异步函数处理方式。
值得注意的是,Internet Explorer 不支持 Promise。要在 IE 中使用 Promise,需要使用 polyfill。
异步函数包括文件读取、服务器请求(ajax)、等待一定时间的函数(例如 setTimeout
)、可能占用大量 CPU 资源的进程(例如对信息进行哈希处理)等。
回调函数
回调函数是作为参数发送到另一个函数的函数。后者期望在一段时间后执行接收到的代码。
以我们将用作示例的 setTimeout
函数为例,它接收一个函数作为参数,该函数在以毫秒为单位定义的时间段结束后执行。
let asincrono = function (cb) {
setTimeout(function () {
cb('second');
}, 3000);
};
console.log('first');
asincrono(function (result) {
console.log(result);
});
console.log('third');
控制台的输出结果将是:
first;
third;
second; // 3 秒后
我们看到,直到经过一段时间(在这个例子中是 setTimeout
指定的 3000
毫秒)之后,"second" 才被打印出来。
在这里,我们使用一个名为 cb
的函数(它可以有任何名称),在异步进程完成后调用它,在这个例子中,是 3 秒的时间间隔。
Promise()
JavaScript Promise 接收一个带有两个参数(resolve
,reject
)的函数,这两个参数都是函数,分别在进程成功执行时调用 resolve
,失败时调用 reject
。
Promise 有三种状态:
- Pending(等待被 resolved 或 rejected)
- Fulfilled(已 resolved)
- Rejected(已 rejected)
用 Promise 重写前面的例子如下所示:
// Promise 使用 'new' 实例化
let asincrono = new Promise(function (resolve, reject) {
setTimeout(function () {
resolve('second');
}, 3000);
});
console.log('first');
asincrono.then(function (result) {
console.log(result);
});
console.log('third');
控制台的输出结果与前一个例子相同。
Promise 的另一个用途是能够知道异步查询何时完成,例如对服务器的查询。
let datosDeServidor = function (url) {
return new Promise(function (resolve, reject) {
let xhr = new XMLHttpRequest();
xhr.open('GET', url);
xhr.onload = function () {
resolve(xhr.responseText); // 通过 'then()' 访问
};
xhr.onerror = function () {
reject(xhr.statusText); // 通过 'catch()' 访问
};
xhr.send();
});
};
datosDeServidor(/* url */)
.then(function (response) {
// 查询完成
console.log(response); // xhr.responseText
})
.catch(function (error) {
// 请求错误
console.log(error); // xhr.statusText
});
我们看到 then()
用于 resolve
,catch()
用于 reject
。
JavaScript 和 NodeJs 中使用的许多库都支持 Promise。例如,用于发送 ajax 请求的 axios
就是基于 Promise 的,因此它的常规用法是:
axios({
/* options */
})
.then(function (response) {
// 请求成功
console.log(response);
})
.catch(function (error) {
// 请求失败
console.log(error);
});
Promise.all()
Promise.all()
接收一个 Promise 数组,并在所有 Promise 都 resolved 后返回一个包含结果的数组。以查询服务器为例,如果我们有几个异步查询,想在它们都完成后再执行某些操作,就可以使用 Promise.all()
。
Promise.all([
datosDeServidor(/* url from server x */),
datosDeServidor(/* url from server y */),
datosDeServidor(/* url from server z */),
])
.then(function (results) {
console.log(results[0]); // 来自服务器 x 的响应
console.log(results[1]); // 来自服务器 y 的响应
console.log(results[2]); // 来自服务器 z 的响应
})
.catch(function (error) {
console.log(error); // 第一个失败的 Promise 的错误
});
JavaScript Promise 为我们处理异步代码提供了更大的灵活性。在 Web 开发中,Promise 最常见的用途之一是能够控制对 Web 服务器的请求(ajax)流,因为根据连接和其他因素,响应的确切时间是不可预测的。