此笔记的内容引用自官网developer.mozilla.org—Promise和developer.mozilla.org—使用Promise。
1. 什么是Promise
Promise 对象是一个代理对象(代理一个值),被代理的值在Promise对象创建时可能是未知的。它允许你为异步操作的成功和失败分别绑定相应的处理方法(handlers)。 这让异步方法可以像同步方法那样返回值,但并不是立即返回最终执行结果,而是一个能代表未来出现的结果的promise对象。
一个 Promise有以下几种状态:
- pending: 初始状态,既不是成功,也不是失败状态。
- fulfilled: 意味着操作成功完成。
- rejected: 意味着操作失败。
pending 状态的 Promise 对象可能触发fulfilled 状态并传递一个值给相应的状态处理方法,也可能触发失败状态(rejected)并传递失败信息。
当其中任一种情况出现时,Promise 对象的 then 方法绑定的处理方法(handlers)就会被调用。
then方法包含两个参数:onfulfilled 和 onrejected,它们都是 Function 类型:
- 当Promise状态为fulfilled时,调用 then 的 onfulfilled 方法;
- 当Promise状态为rejected时,调用 then 的 onrejected 方法。
所以在异步操作的完成和绑定处理方法之间不存在竞争。
因为 Promise.prototype.then 和 Promise.prototype.catch 方法返回promise 对象, 所以它们可以被链式调用。
1
2
3
4
5
6
7
8
9
10
11
12
13
Promise.prototype.then(onFulfilled, onRejected)// 返回一个新的promise
/*例子:*/
p.then(function(value) {
// 当p(一个Promise对象)的状态由pending变成fulfilled时调用
}, function(reason) {
// 当p(一个Promise对象)的状态由pending变成rejected时调用
});
Promise.prototype.catch(onRejected)// 返回一个新的promise
/*例子:*/
p.catch(function(reason) {
// 当p(一个Promise对象)的状态由pending变成rejected时调用
});
2. Promise构造函数
1
new Promise( function(resolve, reject) {...} /* executor */ );
根据官网描述总结以下6条要点:
- 1) Promise构造函数中传入的参数为函数:executor;
- 2) Promise构造函数执行时,立即调用executor函数;
- 3) executor函数有两个函数作为参数:resolve和reject;
- 4) executor函数内部通常会执行异步操作,并在完成后调用resolve或reject;
- 5) resolve被调用时将promise的状态改为fulfilled;
- 6) reject被调用时将promise的状态改为rejected;
注意上述第2条,在Promise构造函数返回新建对象前,executor函数就已经被调用了。
3. 使用Promise
3.1 创建Promise
我们可以用下面的小例子来理解Promise。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
let p1 = new Promise((resolve, reject) => {
// setTimeout模拟异步操作
setTimeout(() => {
let n = -1 + Math.random() * 2;// 随机数-1~1
if (n >= 0) {
console.log('随机数 = ' + n);
console.log('p1状态:fulfilled。');
resolve(n);// 把p1状态设为fulfilled。
} else {
console.log('随机数 = ' + n);
console.log('p1状态:rejected。');
reject(new Error('随机数小于0'));// 把p1状态设为rejected。
}
}, 1000);
});
p1.then(res =>{
// p1的状态若为fulfilled,执行第一个函数,打印n的值;
console.log('n = ' + res);
}, err => {
// p1的状态若为rejected,执行第二个函数,打印错误。
console.log(err);
});
Promise构造函数执行时,立即调用作为参数的executor函数。
在此函数中,我们使用setTimeout()函数来模拟异步操作:1秒钟后,生成一个范围为-1~1的随机数n:
- 如果n>=0,使用
resolve();
把p1(一个Promise对象)的状态设为fulfilled; - 如果n<0,使用
reject()
把p1的状态设为rejected。
然后根据p1的状态执行p1.then()函数:
- p1的状态若由pending变为fulfilled,执行第一个函数,打印n的值;
- p1的状态若由pending变为rejected,执行第二个函数,打印错误。
结果会有两种情况:
1
2
3
随机数 = -0.5353394506291314
p1状态:rejected。
Error: 随机数小于0
1
2
3
随机数 = 0.02490386338275874
p1状态:fulfilled。
n = 0.02490386338275874
3.2 链式调用
方法Promise.prototype.then(onFulfilled, onRejected)
会返回一个新的promise,所以我们可以通过创造一个promise chain来完成连续执行两个或者多个异步操作的需求。
我们在3.1中的小例子基础上修改代码,利用Promise完成这样一个功能:
一秒钟后生成第1个随机数,若它不小于0,则一秒钟后再生成第2个随机数,并把这两个随机数相加。若相加结果仍然不小于0,则打印结果。其余情况则打印错误。
代码如下:
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
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
let count = 0;
let p1 = new Promise((resolve, reject) => {
// setTimeout模拟异步操作
setTimeout(() => {
count++;
let n = -1 + Math.random() * 2;// 随机数-1~1
if (n >= 0) {
console.log('第1个随机数 = ' + n);
console.log('p1状态:fulfilled。');
resolve(n);// 把p1状态设为fulfilled。
} else {
console.log('第1个随机数 = ' + n);
console.log('p1状态:rejected。');
reject(new Error(`第${count}个随机数小于0`));// 把p1状态设为rejected。
}
}, 1000);
});
// promise状态为fulfilled时调用的函数
function Fulfilled(res) {
count++;
// new一个Promise对象
let p = new Promise((resolve, reject) => {
setTimeout(() => {
let n = -1 + Math.random() * 2;// 随机数-1~1
console.log(`第${count}个随机数 = ` + n);
if (n + res >= 0) {
console.log(`p${count}状态:fulfilled。`);
resolve(n+res);// 把返回的promise状态设为fulfilled。
} else {
console.log(`p${count}状态:rejected。`);
reject(new Error(`第${count}个随机数与前面的结果相加小于0`));// 把返回的promise状态设为rejected。
}
}, 1000);
});
return p;// 返回此promise
}
// promise状态为rejected时调用的函数
function Rejected(err) {
count++;
// new一个Promise对象
let p = new Promise((resolve, reject) => {
setTimeout(() => {
console.log(`p${count}状态:rejected。`);
reject(err);// 把返回的promise状态设为rejected。
}, 1000);
});
return p;// 返回此promise
}
let p2 = p1.then(Fulfilled, Rejected);
p2.then((res) => {
console.log('最终结果为:' + res);
}, (err) => {
console.log(err);
});
结果有三种可能:
- 第一种:
- executor函数(作为Promise构造函数的参数)中:第一秒后,生成一个随机数n=0.95。n不小于零,所以执行
resolve(n);
,p1状态被设为fulfilled。 - 执行
p1.then(Fulfilled, Rejected);
中的Fulfilled函数,返回一个Promise对象p:
第二秒后生成第二个随机数n=0.26。两个随机数相加为1.21,不小于零,所以执行resolve(n+res);
把返回的Promise对象p状态设为fulfilled。
注:p2与这个返回的Promise对象p的状态和参数一致。见下方引用的官方解释 - 执行
p2.then()
中的第一个函数,打印结果。1 2 3 4 5
第1个随机数 = 0.9489641211016062 p1状态:fulfilled。 第2个随机数 = 0.26289801708479477 p2状态:fulfilled。 最终结果为:1.211862138186401
- executor函数(作为Promise构造函数的参数)中:第一秒后,生成一个随机数n=0.95。n不小于零,所以执行
- 第二种:
- executor函数中:第一秒后,生成一个随机数n=-0.92。n小于零,所以执行
reject(new Error(/*错误原因*/))
,p1状态被设为rejected。 - 执行
p1.then(Fulfilled, Rejected);
中的Rejected函数,返回一个Promise对象p:
第二秒后执行reject(err);
把返回的Promise对象p状态设为rejected。 - 执行
p2.then()
中的第二个函数,打印err。1 2 3 4
第1个随机数 = -0.9151184862276849 p1状态:rejected。 p2状态:rejected。 Error: 第1个随机数小于0
- executor函数中:第一秒后,生成一个随机数n=-0.92。n小于零,所以执行
- 第三种:
- executor函数中:第一秒后,生成一个随机数n=0.41。n不小于零,所以执行
resolve(n);
,p1状态被设为fulfilled。 - 执行
p1.then(Fulfilled, Rejected);
中的Fulfilled函数,返回一个Promise对象p:
第二秒后生成第二个随机数n=-0.69。两个随机数相加为-0.28,小于零,所以执行reject(new Error(/*错误原因*/))
把返回的Promise对象p状态设为rejected。 - 执行
p2.then()
中的第二个函数,打印err。1 2 3 4 5
第1个随机数 = 0.4141558202079687 p1状态:fulfilled。 第2个随机数 = -0.6860477930831919 p2状态:rejected。 Error: 第2个随机数与前面的结果相加小于0
- executor函数中:第一秒后,生成一个随机数n=0.41。n不小于零,所以执行
在上面案例中,为了清晰地描述过程,使用了变量let p2
。我们可以采用更简单的形式:
1
2
3
4
5
p1.then(Fulfilled, Rejected).then((res) => {
console.log('最终结果为:' + res);
}, (err) => {
console.log(err);
});
如果我们想让三个随机数相加,只需要修改上面的代码:
1
2
3
4
5
p1.then(Fulfilled, Rejected).then(Fulfilled, Rejected).then((res) => {
console.log('最终结果为:' + res);
}, (err) => {
console.log(err);
});
下面显示了运行代码后出现的其中一种情况:
1
2
3
4
5
6
7
第1个随机数 = 0.9745031598411451
p1状态:fulfilled。
第2个随机数 = 0.3137366861790132
p2状态:fulfilled。
第3个随机数 = 0.015714137953589447
p3状态:fulfilled。
最终结果为:1.3039539839737477
关于then()方法返回的Promise,引用官网的描述:
then方法返回一个Promise,而它的行为与then中的回调函数的返回值有关:
- 如果then中的回调函数返回一个值:
那么then返回的Promise将会成为接受状态,并且将返回的值作为接受状态的回调函数的参数值。- 如果then中的回调函数抛出一个错误:
那么then返回的Promise将会成为拒绝状态,并且将抛出的错误作为拒绝状态的回调函数的参数值。- 如果then中的回调函数返回一个已经是接受状态的Promise:
那么then返回的Promise也会成为接受状态,并且将那个Promise的接受状态的回调函数的参数值作为该被返回的Promise的接受状态回调函数的参数值。- 如果then中的回调函数返回一个已经是拒绝状态的Promise:
那么then返回的Promise也会成为拒绝状态,并且将那个Promise的拒绝状态的回调函数的参数值作为该被返回的Promise的拒绝状态回调函数的参数值。- 如果then中的回调函数返回一个未定状态(pending)的Promise:
那么then返回Promise的状态也是未定的,并且它的终态与那个Promise的终态相同;同时,它变为终态时调用的回调函数参数与那个Promise变为终态时的回调函数的参数是相同的。