ES6学习笔记16:Promise

Promise基本用法

Promise出现的动机是为了解决回掉函数嵌套问题而出现的,最开始由三方库实现,ES6现在将其加入了标准当中。

Promise对象是用于异步计算的。一个Promise表示了一个可能在现在,在将来,或者永远无法获取的值。一个Promise有3个可能的状态,分别是pending(初始态,未满足也未被拒绝),fulfilled(满足态,操作成功执行完毕)和rejected(拒绝态,操作失败)。

注意Promise的状态只能从pending到fulfilled或者从pending到rejected,而不能从fulfilled到rejected或者反过来。

1
2
3
let p = new Promise((resolve, reject) => {
// do something.
});

Promise需要一个函数做参数,该函数有两个参数,这两个参数也是函数,分别是resolve和reject,resolve函数会在当Promise成功执行(fulfilled)之后该执行,reject函数会在当失败之后(rejected)执行。

来看一个完整的示例。

1
2
3
4
5
6
7
let p = new Promise((resolve, reject) => {
console.log('async started.');
resolve();
});
p.then(() => console.log('async ended.')).catch(err => console.log(err));
// async started.
// async ended.

上面的代码中我们先定义了一个Promise,该Promise的逻辑很简单,输出'async started.',输出之后,执行指定的resolve函数。

在定义了p之后,我们链式调用了then函数和catch函数,简言之,这两个函数都接收一个函数作为参数,then函数接收的函数参数表示执行成功(fulfilled)的逻辑,catch函数接收的函数参数表示执行失败的逻辑,具体的调用逻辑在Promise定义的时候确定(thencatch两个函数的参数分别是Promise中的resolvereject)。

then

Promise.prototype.then()方法返回一个Promise,实际上可以有两个参数,第一个是fulfilled状态时被执行的函数,第二个是rejected状态时被执行的函数,不过第二个参数可以省略。

此外,由于then方法返回的是一个Promise,因此then方法可以是链式调用,当前一个then的参数函数返回的是一个非Promise类型的值时,该值会作为下一个then方法的参数函数的参数,如下面代码所示。

1
2
3
4
5
6
7
8
9
10
11
12
13
let p = new Promise((resolve, reject) => {
console.log('async started.');
resolve(1);
});
p.then((foo) => {
console.log(`number is ${foo++}.`)
return foo;
}).then((bar) => {
console.log(`number is ${bar}.`)
}).catch(err => console.log(err));
// async started.
// number is 1.
// number is 2.

而当前一个then的参数函数返回的是一个Promise类型的值时,后续紧跟的那个函数会等带该Promise值改变状态时才会被调用,就相当于后续紧跟的那个then方法是前一个Promise对象函数调用的then一样。

1
2
3
4
5
6
7
8
9
10
11
12
13
let p = new Promise((resolve, reject) => {
console.log('async started.');
resolve(1);
});
p.then((foo) => {
console.log(`number is ${foo++}.`)
return new Promise((resolve, reject) => resolve(3));
}).then((bar) => {
console.log(`number is ${bar}.`)
}).catch(err => console.log(err));
// async started.
// number is 1.
// number is 3.

catch

Promise.prototype.catch()方法返回一个Promise并且只处理拒绝态(rejected)。Promiess.prototype.catch(onRejected)跟调用Promiess.prototype.then(undefined, onRejected)效果是一样的。

1
2
3
4
5
6
7
let p = new Promise((resolve, reject) => {
console.log('async started.');
reject(new Error('this is an error.'));
});
p.then((foo) => console.log(`number is ${foo}.`)).catch(err => console.log(err));
// async started.
// Error: this is an error.(…)

all

Promise.all()方法接收一个iterabal对象,后面的处理行为类似处理单个Promise,也是链式调用then()方法和catch()方法。

1
2
3
4
5
6
7
var p1 = Promise.resolve(3);
var p2 = 1337;
var p3 = new Promise((resolve, reject) => {
setTimeout(resolve, 100, "foo");
});
Promise.all([p1, p2, p3]).then(value => console.log(value));
// [3, 1337, "foo"]

由上可以看出all()方法会等待所有Promise执行完成,然后以数组的形式处理返回值。此外catch()方法的使用也类似。最后如果有一个Promise执行被拒绝了,其他Promise也会被拒绝,而且如果你的拒绝之后的所有其他Promise都不会被执行(Fail-Fast行为,参考3)。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
let p1 = new Promise((resolve, reject) => {
setTimeout(resolve, 100, 'one');
});
let p2 = new Promise((resolve, reject) => {
setTimeout(resolve, 200, 'two');
});
let p3 = new Promise((resolve, reject) => {
setTimeout(resolve, 300, 'three');
});
let p4 = new Promise((resolve, reject) => {
setTimeout(resolve, 400, 'four');
});
let p5 = new Promise((resolve, reject) => {
setTimeout(reject, 350, 'reject five');
});
Promise.all([p1, p2, p3, p4, p5]).then(value => {
console.log(value);
}, reason => {
console.log(reason);
});

知识点总结

  1. Promise的基本概念,三个状态和基本用法;
  2. Promise.prototype.then()方法的用法;
  3. Promise.prototype.catch()方法的用法;
  4. Promise.all()方法的用法。

参考

  1. BabelJS - Learn ES2015
  2. Promise - MDN
  3. Promise.all() - MDN
  4. ECMAScript 6 入门 Promise对象 - 阮一峰