YeaseonZhang

Promise深入理解

什么是Promise

Promise的核心理念是一个异步操作的结果,Promise包含两部分

  • [[PromiseStates]]
  • [[PromiseValue]]

Promise状态三种可能:

  • pending: 悬而未决
  • resolved: 决定
  • rejected: 拒绝

异步性

1
2
3
4
5
6
7
8
9
const p = new Promise((resolve, reject) => {
resolve('success');
});

p.then((value) => {
console.log(value);
});

console.log('Called first ?');

Promisethen()具有异步性,当执行到.then()部分,这部分会自动进入到Promise的异步事件队列,不会阻塞同步代码的执行,所以Called first?先输出。

输出结果

1
2
Called first ?
success

立即执行性

1
2
3
4
5
6
7
8
9
10
const p = new Promise((resolve, reject) => {
console.log('Create a promise');
resolve('success');
});

p.then((value) => {
console.log(value);
});

console.log('After new Promise');

Promise异步性,我们可以推断出,After new Promise,会先于then()方法中的输出。同时Promise立即执行性,定义了promise定义的同时就会立即执行,并不依赖于then()的调用。而且与函数明显不同,函数需要主动调用才会执行。

输出结果

1
2
3
Create a promise
After new Promise
success

Promise的状态

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
const p1 = new Promise((resolve, reject) => {
resolve(1);
});

const p2 = new Promise((resolve, reject) => {
setTimeout(() => {
resolve(2);
}, 500);
});
const p3 = new Promise((resolve, reject) => {
setTimeout(() => {
reject(3);
}, 500);
});

console.log(p1);
console.log(p2);
console.log(p3);

setTimeout(() => {
console.log('p2-setTimeout:', p2);
}, 501);

setTimeout(() => {
console.log('p3-setTimeout:', p3);
}, 501);

p1.then((value) => {
console.log('p1-then:', value);
});

p2.then((value) => {
console.log('p2-then:', value);
});

p3.catch((err) => {
console.log('p3-catch', err);
});

Promise创建完成时,处于 pending 状态;
Promise执行了resolve方法,Promise对象的状态会变成 resolved 状态;
Promise执行了reject方法,Promise对象的状态会变成 rejected 状态;

先输出

1
2
3
Promise {[[PromiseStatus]]: "resolved", [[PromiseValue]]: 1}
Promise {[[PromiseStatus]]: "pending", [[PromiseValue]]: undefined}
Promise {[[PromiseStatus]]: "pending", [[PromiseValue]]: undefined}

然后输出

1
p1-then: 1

500ms之后,p2p3Promise状态被执行,then被触发,输出:

1
2
p2-then: 2
p3-catch 3

最后会输出:

1
2
p2-setTimeout: Promise {[[PromiseStatus]]: "resolved", [[PromiseValue]]: 2}
p3-setTimeout: Promise {[[PromiseStatus]]: "rejected", [[PromiseValue]]: 3}

状态不可逆性

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
const p1 = new Promise((resolve, reject) => {
resolve('p1 success 1');
resolve('p1 success 2');
});

const p2 = new Promise((resolve, reject) => {
resolve('p2 success');
reject('p2 reject');
});

p1.then((value) => {
console.log(value);
});

p2.then((value) => {
console.log(value);
}, (err) => {
console.log(err);
});

Promise一旦变成resolved或是rejected,这个状态就不能再次变化,这就是Promise的不可逆性。

输出

1
2
p1 success 1
p2 success

链式调用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
const p = new Promise(((resolve, reject) => {
resolve(1);
}));

p.then((value) => {
console.log(value);
return value * 2;
}).then((value) => {
console.log(value);
}).then((value) => {
console.log(value);
return Promise.resolve('resolve');
}).then((value) => {
console.log(value);
return Promise.reject('reject');
}).then((value) => {
console.log('resolve: ', value);
}, (err) => {
console.log('reject: ', err);
});

Jquery对象链式调用是执行jquery方法之后,会继续返回jquery对象;类似这个原理,Promise对象的then方法会返回一个新的Promise对象,这样就可以继续调用then方法。同样then方法中的两个参数还是fulfilled对象和rejected对象。

return一个值或者Promise.resolve()时,状态为 resolved
throw一个异常或者return Promise.reject(),状态为 rejected

: 当then()方法中没有return时,会默认返回undefined,状态为 resolved

输出

1
2
3
4
5
1
2
undefined
resolve
reject: reject

Promise中的异常处理

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
const p1 = new Promise((resolve, reject) => {
foo.bar();
resolve(1);
});

p1.then((value) => {
console.log('p1 then value: ', value);
}, (err) => {
console.log('p1 then err: ', err);
}).then((value) => {
console.log('p1 then then value: ', value);
}, (err) => {
console.log('p1 then then err: ', err);
});

var p2 = new Promise((resolve, reject) => {
resolve(2);
});

p2.then((value) => {
console.log('p2 then value: ', value);
foo.bar();
}, (err) => {
console.log('p2 then err: ', err);
}).then((value) => {
console.log('p2 then then value: ', value);
}, (err) => {
console.log('p2 then then err: ', err);
return 1;
}).then((value) => {
console.log('p2 then then then value: ', value);
}, (err) => {
console.log('p2 then then then err: ', err);
});

Promise中的异常会交给then方法中的第二个回调函数处理,一旦处理完成,会继续返回一个Promise对象给后续then方法。

可以看到输出是p1p2交替输出的,这个并不一定是交替输出,取决于执行情况,也可能是p2先输出。

输出

1
2
3
4
5
p1 then err:  ReferenceError: foo is not defined
p2 then value: 2
p1 then then value: undefined
p2 then then err: ReferenceError: foo is not defined
p2 then then then value: 1

Promise.resolve()

Promise.resolve()语法:

1
Promise.resolve(value);
  • value: 用来解析待返回promise对象的参数,既可以是一个promise对象,也可以是一个thenable(即带有then方法)。
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
const p1 = Promise.resolve(1);
const p2 = Promise.resolve(p1);
const p3 = new Promise((resolve, reject) => {
resolve(1);
});

const p4 = new Promise((resolve, reject) => {
resolve(p1);
});

// console.log(p1);
// console.log(p2);
// console.log(p3);
// console.log(p4);

console.log(p1 === p2);
console.log(p1 === p3);
console.log(p1 === p4);
console.log(p3 === p4);

p4.then((value) => {
console.log('p4 = ', value);
});

p2.then((value) => {
console.log('p2 = ', value);
});

p1.then((value) => {
console.log('p1 = ', value);
})

p1: 接收了一个普通值1,所以会返回一个resolved状态的Promise对象,并且值为1
p2: 接收了一个promise对象p1,会直接返回这个promise对象。
p3p4: 通过new方式创建了一个新的promise对象。

所以,p1 === p2,p3p4都是创建的新对象,所以自身和其他三个对象都不相等。

输出

1
2
3
4
true
false
false
false

但是后三个输出是:

1
2
3
p2 = 1
p1 = 1
p4 = 1

很有意思的是,明明是p4先执行的then方法,但是却是后输出的。

在定义完4个promise对象时,状态分别为:

1
2
3
4
Promise {[[PromiseStatus]]: "resolved", [[PromiseValue]]: 1}
Promise {[[PromiseStatus]]: "resolved", [[PromiseValue]]: 1}
Promise {[[PromiseStatus]]: "resolved", [[PromiseValue]]: 1}
Promise {[[PromiseStatus]]: "pending", [[PromiseValue]]: undefined}

很明显,p4pending状态,与其他三个不同,因为p4resolve中接收的参数是一个promise对象p1resolve会对p1进行“拆箱”操作,这个过程是异步的。

:把基本数据类型转换为对应的引用类型的操作称为装箱,把引用类型转换为基本的数据类型称为拆箱。

resolve() & reject() 的区别

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
const p1 = new Promise((resolve, reject) => {
resolve(Promise.resolve('resolve'));
});

const p2 = new Promise((resolve, reject) => {
resolve(Promise.reject('reject'));
});

const p3 = new Promise((resolve, reject) => {
reject(Promise.resolve('resolve'));
});

p1.then((value) => {
console.log('p1-resolve:', value);
}, (err) => {
console.log('p1-reject:', err);
});

p2.then((value) => {
console.log('p2-resolve:', value);
}, (err) => {
console.log('p2-reject:', err);
});

p3.then((value) => {
console.log('p3-resolve:', value);
}, (err) => {
console.log('p3-reject:', err);
});

resolve方法和reject方法除了在状态上有区别,处理方式上也有区别,resolve方法上面提到了会对promise对象“拆箱”,但是reject方法不会。

p3没有“拆箱”操作,所以会最先输出,直接调用reject方法,输出Promise.resolve('resolve')对象
p1会“拆箱”得到Promise.resolve('resolve')这个promise对象的状态和值,调用resolve方法。
p2会“拆箱”得到Promise.reject('reject')这个promise对象的状态和值,因为得到的状态是rejected,所以会调用reject方法。

输出

1
2
3
p3-reject: Promise {[[PromiseStatus]]: "resolved", [[PromiseValue]]: "resolve"}
p1-resolve: resolve
p2-reject: reject

all() & race() & then() 区别

Promise.all()语法:

1
Promise.all(iterable);

Promise.race()语法:

1
Promise.race(iterable)
  • iterable: 可迭代对象,例如一个数组。
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
let timerPromisefy = (delay) => {
return new Promise((resolve) => {
setTimeout(() => {
resolve(delay);
}, delay);
});
}

let timerPromisefyReject = (delay) => {
return new Promise((resolve, reject) => {
setTimeout(() => {
reject(delay);
}, delay);
});
}

console.time('Promise all');
Promise.all([
timerPromisefy(1),
timerPromisefy(7),
timerPromisefy(10),
timerPromisefy(9)
]).then((value) => {
console.timeEnd('Promise all');
});

console.time('Promise then');
timerPromisefy(1).then(() => {
return timerPromisefy(7)
}).then(() => {
return timerPromisefy(10)
}).then(() => {
return timerPromisefy(9)
}).then(() => {
console.timeEnd('Promise then')
});

console.time('Promise race');
Promise.race([
timerPromisefy(1),
timerPromisefy(7),
timerPromisefy(10),
timerPromisefy(9)
]).then(value => {
console.timeEnd('Promise race');
});

Promise.all()方法返回一个Promise,当iterable参数中的 promise 并行执行,当所有 promise 都已经 resolve 了,返回 resolved 状态。当传递的 promise 包含一个 reject ,则返回 rejected 状态。
如果Promise.all()返回 resolved , 那么执行时间取决于执行最最慢的那个 promise;如果Promise.all()返回 rejected , 执行时间取决于第一个返回 rejected 的执行时间。

Promise.race()方法返回一个Promise,当iterable参数中只要有一个 promise 状态被判定了,那么就返回该状态。
所以Promise.race()的执行时间取决于执行最快的那个 promise

Promise.then()方法的执行时间,是每个链式调用总时间之和。

输出

1
2
3
Promise race: 2.3232421875ms
Promise all: 3.675048828125ms
Promise then: 31.32373046875ms