在实际的使用中,有非常多的应用场景我们不能立即知道应该如何继续往下执行。最常见的一个场景就是ajax请求。通俗来说,由于网速的不同,可能你得到返回值的时间也是不同的,这个时候我们就需要等待,结果出来了之后才知道怎么样继续下去。
在ajax的原生实现中,利用了onreadystatechange事件,当该事件触发并且符合一定条件时,才能拿到想要的数据,之后才能开始处理数据。
// 简单的ajax原生实现
var url = 'https://hq.tigerbrokers.com/fundamental/finance_calendar/getType/2017-02-26/2017-06-10';
var result;
var XHR = new XMLHttpRequest();
XHR.open('GET', url, true);
XHR.send();
XHR.onreadystatechange = function() {
if (XHR.readyState == 4 && XHR.status == 200) {
result = XHR.response;
console.log(result);
}
}
这样做看上去并没有什么麻烦,但如果这个时候,我们还需要另外一个ajax请求,这个新ajax请求的其中一个参数,得从上一个ajax请求中获取,这个时候我们就不得不等待上一个接口请求完成之后,再请求后一个接口。如下:
var url = 'https://hq.tigerbrokers.com/fundamental/finance_calendar/getType/2017-02-26/2017-06-10';
var result;
var XHR = new XMLHttpRequest();
XHR.open('GET', url, true);
XHR.send();
XHR.onreadystatechange = function() {
if (XHR.readyState == 4 && XHR.status == 200) {
result = XHR.response;
console.log(result);
// 伪代码
var url2 = 'http:xxx.yyy.com/zzz?ddd=' + result.someParams;
var XHR2 = new XMLHttpRequest();
XHR2.open('GET', url, true);
XHR2.send();
XHR2.onreadystatechange = function() {
...
}
}
}
当出现第三个ajax(甚至更多)仍然依赖上一个请求时,我们的代码就变成了一场灾难。这场灾难,往往也被称为回调地狱
。
什么是Promise
Promise,简单说就是一个容器,里面保存着某个未来才会结束的事件(通常是一个异步操作)的结果。
一个简单的Promise实例如下:
const promise = new Promise(function(resolve, reject) {
// ... some code
if (/* 异步操作成功 */){
resolve(value);
} else {
reject(error);
}
});
Promise的三种状态
Promise有三种状态
,它们分别是:
- pending: 等待中,或者进行中,表示还没有得到结果
- resolved(Fulfilled): 已经完成,表示得到了我们想要的结果,可以继续往下执行
- rejected: 也表示得到结果,但是由于结果并非我们所愿,因此拒绝执行
这三种状态不受外界影响,而且状态只能从pending改变为resolved或者rejected,并且不可逆。在Promise对象的构造函数中,将一个函数作为第一个参数。
Promise的构造函数
Promise构造函数接受一个函数作为参数,这个函数,就是用来处理Promise的状态变化。,该函数的两个参数分别是resolve和reject。
resolve函数的作用
是,将Promise对象的状态从“未完成”变为“成功”(即从 pending 变为 resolved),在异步操作成功时调用,并将异步操作的结果,作为参数传递出去;reject函数的作用
是,将Promise对象的状态从“未完成”变为“失败”(即从 pending 变为 rejected),在异步操作失败时调用,并将异步操作报出的错误,作为参数传递出去。
Promise的then函数
Promise实例生成以后,可以用then方法分别指定resolved状态和rejected状态的回调函数。
promise.then(function(value) {
// success
}, function(error) {
// failure
});
then方法可以接受两个回调函数作为参数。第一个回调函数是Promise对象的状态变为resolved时调用,第二个回调函数是Promise对象的状态变为rejected时调用。其中,第二个函数是可选的,不一定要提供。这两个函数都接受Promise对象传出的值作为参数。
下面是一个Promise对象的简单例子。
function timeout(ms) {
return new Promise((resolve, reject) => {
setTimeout(resolve, ms, 'done');
});
}
timeout(100).then((value) => {
console.log(value);
});
上面代码中,timeout方法返回一个Promise实例,表示一段时间以后才会发生的结果。过了指定的时间(ms参数)以后,Promise实例的状态变为resolved,就会触发then方法绑定的回调函数。
Promise新建后会立即执行
let promise = new Promise(function(resolve, reject) {
console.log('Promise');
resolve();
});
promise.then(function() {
console.log('resolved.');
});
console.log('Hi!');
// Promise
// Hi!
// resolved
上面代码中,Promise 新建后立即执行,所以首先输出的是Promise。然后,then方法指定的回调函数,将在当前脚本所有同步任务执行完才会执行
,所以resolved最后输出。
Promise的all方法
Promise.all方法用于将多个 Promise 实例,包装成一个新的 Promise 实例。
const p = Promise.all([p1, p2, p3]);
上面代码中,Promise.all方法接受一个数组
作为参数,p1、p2、p3都是 Promise 实例,如果不是,就会先调用Promise.resolve方法,将参数转为 Promise 实例,再进一步处理。(Promise.all方法的参数可以不是数组,但必须为可迭代对象,且返回的每个成员都是 Promise 实例。)
Promise.all([
new Promise((resolve, reject) => {
setTimeout(() => {
resolve('我是第一次请求')
}, 200)
}),
new Promise((resolve, reject) => {
setTimeout(() => {
resolve('我是第二次请求')
}, 100)
}),
]).then(results => {
console.log(results);
})