promise
是 ECMAScript6
新加的一个概念。
本文将使用简洁的文字介绍 promise
,仅介绍大概情况。
更多详情请参考结尾的推荐文章。
好了,正文开始 !
简介
Promise
是抽象 异步操作
对象以及对其进行一系列操作的组件。
Promise
最大的功能是把 异步操作
变为 同步操作
。即只有 异步操作
完成后,才进行后面的操作。
Promise
定义了一组 上述操作
相关的接口和方法,直接使用即可。
Promise 状态
使用 new promise
实例化的 promise对象
有以下三个状态:
Fulfilled
resolve(成功)
时,此时会调用 onFulfilled
。
Rejected
reject(失败)
时,此时会调用 onRejected
。
Pending
既不是 resolve
也不是 reject
状态,也就是 promise
对象刚被创建后的初始化状态。
方法
创建 promise 实例
创建实例有三种方法:
new Promise()
;Promise.resolve
;Promise.reject
;1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19// 方法1
var promise1 = new Promise(function(resolve, reject) {
resolve('11');
reject(new Error('22'));
});
// 方法2
var promise2 = Promise.resolve('1');
// 等效于
var promise2 = new Promise(function(resolve) {
resolve('1');
});
// 方法3
var promise3 = Promise.reject('2');
// 等效于
var promise3 = new Promise(function(null, reject) {
reject('2');
});
resolve
Promise.resolve
:创建实例;- 返回
promise
对象,主要分下面3种情况:- 接收参数为
promise
对象:返回的还是接收到的promise
对象; - 接收参数为
thenable
类型的对象:返回一个新的promise
对象,这个对象有具有一个.then
方法。更多thenable
的信息,点我查看 ; - 接收参数为
其他类型
:返回一个将该对象作为值的新的promise
对象;
- 接收参数为
reject
Promise.reject
:创建实例;- 返回一个使用了接收到的值进行了
reject
的新的promise
对象; - 传给
Promise.reject
的值应该是一个Error
类型的对象; - 另外,和
Promise.resolve
不同的是,即使Project.reject
接收的是一个promise
对象,该函数也还是会返回一个全新的promise
对象;
then
promise.then
:promise
中最常用的方法;在
promise
对象变为resolve
或reject
后,执行相应的回调函数;then
中return
的值,作为参数传入链式方法
里的下一个方法;1
2
3
4
5
6
7
8
9
10
11
12
13
14// 常规用法
var promise = new Promise(function(resolve, reject) {
//...
resolve('11');
reject(new Error('Boom!'));
});
promise.then(function(value) {
console.log(value) // 11
}, function(error) {
console.log(error);
})
// 链式调用 return 的值作为下一个方法的参数
// 见后面 链式方法
catch
promise.catch
:处理异常的方法,和reject
类似;包含
promise
对象变为reject
、手动throw new Error()
、系统抛出异常
三种情况;推荐使用
catch
,因为对于then(f1,f2).catch()
这种情况,如果f1
抛出异常,f2
是捕获不了的,链式方法后面的catch
才能捕获到,或者改成then(f1,f2).then(null,f3)
这样,f3
也能捕获到;1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29// 常规用法
var promise = Promise.reject(new Error("BOOM!"));
promise.catch(function(value) {
console.log(value); // Error: BOOM!
})
// 等效于 reject
promise.then(function(value){
console.log(value);
},function(error){
console.log(error);
})
// 等效于下面的写法,推荐用下面的写法
promise.then(function(value){
console.log(value);
}).catch(function(error){
console.log(error);
})
// 推荐用 catch 的原因: reject 和 catch 都只能捕获上一级的错误
// 如果用 reject 的话,同级的 resolve 中抛出异常的话,reject 就不能捕获了
// 而如果用 catch 话,因为 catch 是链式方法的下一级,所以可以捕获到上一级 resolve 中的异常
// 用链式方法 + reject 的话也行,效果和 catch 一样
// 下面的写法,和上面的 catch 方法效果一样,不过一般不这样写,一般用 catch
promise.then(function(value){
console.log(value)
}).then(null, function(error){
console.log(error);
})
all
Promise.all
:传入一个promise
对象的数组作为参数。只有当数组里的 所有
promise
对象 全部 变为resolve
时,才会调用then
里相应的回调函数方法,then
参数为 所有promise
返回值组成的 数组。只要有一个
promise
对象变为reject
,就会调用catch
里的回调函数方法,catch
参数为reject
的promise
返回值。数组中的多个
promise
一起 同时开始,并行执行 。所以顺序是:
(arr[0] | arr[1] | arr[2+...]) --> then
或者arr[0] | arr[1] --> catch | arr[2+...]
。1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22var promise1 = new Promise(function(resolve, reject){
//...
resolve('1');
reject(new Error('Boom!'));
});
var promise2 = new Promise(function(resolve, reject){
//...
resolve('2');
reject(new Error('Boom!'));
});
var promise3 = new Promise(function(resolve, reject){
//...
resolve('3');
reject(new Error('Boom!'));
});
var promises = [promise1, promise2, promise3];
Promise.all(promises).then(function(value){
console.log(value); // ["1", "2", "3"]
}).catch(function(error){
console.log(error);
});
race
Promise.race
:和Promise.all
类似,区别是 只要有一个promise
对象变为resolve
或reject
时,就会调用then
或catch
方法;数组中的多个
promise
一起 同时开始,并行执行 (所以第一个执行完的不一定是arr[0]
)。then/catch
方法的参数为第一个执行完的promise
返回的值。所以顺序是:
arr[x] --> then/catch | arr[0] | arr[1] | arr[2+...]
。1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22var promise1 = new Promise(function(resolve, reject){
//...
resolve('1');
reject(new Error('Boom!'));
});
var promise2 = new Promise(function(resolve, reject){
//...
resolve('2');
reject(new Error('Boom!'));
});
var promise3 = new Promise(function(resolve, reject){
//...
resolve('3');
reject(new Error('Boom!'));
});
var promises = [promise1, promise2, promise3];
Promise.race(promises).then(function(value){
console.log(value); // 1
}).catch(function(error){
console.log(error);
});
allSettled
ES2020
提出的新方法。和
Promise.all
、Promise.race
类似,区别是无论每个promise
是resolve
还是reject
,只有当 所有promise
都执行完成 后,才会调用then
函数。then
函数的参数为一个数组,该数组包含原promises
集中每个promise
的结果。对于每个结果对象,都有一个
status
字符串。如果它的值为fulfilled
,则结果对象上存在一个value
。如果值为rejected
,则存在一个reason
。value
(或reason
)反映了每个promise
决议(或拒绝)的值。1
2
3
4
5
6
7
8
9
10const promise1 = Promise.resolve(3);
const promise2 = new Promise((resolve, reject) => setTimeout(reject, 100, 'foo'));
const promises = [promise1, promise2];
Promise.allSettled(promises).
then((results) => results.forEach((result) => console.log(result.status)));
// expected output:
// "fulfilled"
// "rejected"
Advanced
catch 和 reject 是一样的
- 上面已经说过,
catch
和reject
是一样的,只是写法不同; - 不过从
链式方法
的写法角度来说,catch
写在下一级,能够捕获上一级中reject
无法捕获的resolve
中的异常,所以,推荐使用catch
写法;
thenable
thenable
指的是一个具有.then
方法的对象;Promise.resolve
可以将thenable
对象转换为promise
对象,返回的是一个新的promise
对象;- 更多
thenable
的信息,点我查看 ;
链式方法
由于
then
和catch
都返回了一个新的promise
对象,因此它们可以用.
的方式进行链式调用;then
返回的是新的promise
对象,then
里面的方法返回的是 链式方法中下一个方法的传入参数;更多详情,点我查看 ;
1
2
3
4
5
6
7
8
9
10
11// then 返回的 bpromise 是一个 promise 对象
// then 里面函数返回的 value1*2 是链式方法中下一个方法的传入参数(value2)
var apromise = new Promise(function(resolve) {
resolve(100);
});
var bpromise = apromise.then(function(value1){
return value1 * 2;
});
bpromise.then(function(value2) {
console.log(value2); // 200
})
示例
使用 ajax + promise 来读取并操作数据
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24function getURL(URL) {
return new Promise(function(resolve, reject) {
var xhr = new XMLHttpRequest();
xhr.open('GET', URL, true);
xhr.onload = function () {
if (xhr.status === 200) {
resolve(xhr.responseText);
} else {
reject(new Error(xhr.statusText));
}
};
xhr.onerror = function () {
reject(new Error(xhr.statusText));
};
xhr.send(null);
});
}
// 运行示例
var url = "http://httpbin.org/get";
getURL(url).then(function onFulfilled(value) {
console.log(value);
}).catch(function onRejected(error) {
console.log(error);
});