ES6之Promise

Promise基础知识

Promise相当于异步操作结果的占位符,它不会去订阅一个事件,也不会传递一个回调函数给目标函数,而是让函数返回一个Promise:

let promise = readFile('example.txt')

上例子中,readFile不会立即开始读取文件,函数会返回一个表示异步读取操作的Promise对象,未来对这个对象的操作完全取决于Promise的声明周期。

Promise生命周期

生命周期有三个:pending、fullfilled、rejected

所有promise都有then方法,它接受两个参数:第一个当Promise的状态变为fullfilled时要调用的函数,与异步操作相关的附加数据都会传递给这个完成函数;第二个是当Promise的状态变为rejected时要调用的函数,其与完成时调用的函数类似,所有与失败相关的附加数据都会传递这个拒绝函数;

1
2
3
4
5
6
7
8
let promise = readFile("example.txt");
promise.then(function(contents) {
// fulfillment
console.log(contents);
}, function(err) {
// rejection
console.error(err.message);
});

如果一个对象实现了上面的then方法,那这个对象我们称之为thenable对象。所有的Promise对象都是thenable对象,但并非所有thenable对象都是Promise。

Promise被解决(resolved)时执行,这些任务最终会被加入到一个为Promise量身定制的独立队列中,而不会立即执行,当前面的任务完成后其才被调用;

1
2
3
4
5
var p = new Promise(function(resolve, reject){resolve()});
p.then(function(){console.log('do')});
console.log('do1');
// do1
// do

创建未完成的Promise

Promise的执行器会立即执行,然后执行后续流程中的代码;当Promise resolved之后会把任务加入到任务队列尾部,即调用resolve()后出触发一个异步操作,传入then和catch方法的函数会被添加到任务队列中并异步执行;

1
2
3
4
5
6
7
8
9
10
11
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.resolve() / Promise.reject()

除了上面的方法直接创建已处理的Promise;Promise的resolve和reject方法还可以接受非Promise的Thenable对象作为参数。如果传入非Promise的Thenable对象,则这些方法会创建一个新的Promise,并在then函数中被调用;

1
2
3
4
5
6
7
8
9
let thenable = {
then: function(resolve, reject) {
resolve(42);
}
};
let p1 = Promise.resolve(thenable);
p1.then(function(value) {
console.log(value); // 42
});

全局的Promise拒绝处理

为了防止有些没有拒绝处理的promise的错误被忽略掉,可以用全局的Promise拒绝处理。

浏览器环境的拒绝处理

浏览器有两个事件来识别未处理的拒绝的;

  • unhandledrejection 在一个事件循环中,当Promise被拒绝,并且没有提供拒绝处理程序时,触发该事件
  • rejecttionhandled 在一个事件循环后,当Promise被拒绝时,若拒绝处理程序被调用,触发该事件;
1
2
3
4
5
6
7
8
9
10
11
12
let rejected;
window.onunhandledrejection = function(event) {
console.log(event.type); // "unhandledrejection"
console.log(event.reason.message); // "Explosion!"
console.log(rejected === event.promise); // true
};
window.onrejectionhandled = function(event) {
console.log(event.type); // "rejectionhandled"
console.log(event.reason.message); // "Explosion!"
console.log(rejected === event.promise); // true
};
rejected = Promise.reject(new Error("Explosion!"));

串联Promise

1
2
3
4
5
6
7
8
9
10
let p1 = new Promise(function(resolve, reject) {
resolve(42);
});
p1.then(function(value) {
console.log(value);
}).then(function() {
console.log("Finished");
});
// 42
// Finished

务必在Promise链的末尾留一个有拒绝处理程序保证能够正确处理所有可能发生的错误。

Promise链的返回值

1
2
3
4
5
6
7
8
9
let p1 = new Promise(function(resolve, reject) {
resolve(42);
});
p1.then(function(value) {
console.log(value); // 42
return value + 1;
}).then(function(value) {
console.log(value); // 43
});

在Promise链中返回Promise

1
2
3
4
5
6
7
8
9
10
11
12
13
14
let p1 = new Promise(function(resolve, reject) {
resolve(42);
});
let p2 = new Promise(function(resolve, reject) {
resolve(43);
});
p1.then(function(value) {
// first fulfillment handler
console.log(value); // 42
return p2;
}).then(function(value) {
// second fulfillment handler
console.log(value); // 43
});

响应多个Promise

Promise.all

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
let p1 = new Promise(function(resolve, reject) {
resolve(42);
});
let p2 = new Promise(function(resolve, reject) {
resolve(43);
});
let p3 = new Promise(function(resolve, reject) {
resolve(44);
});
let p4 = Promise.all([p1, p2, p3]);
p4.then(function(value) {
console.log(Array.isArray(value)); // true
console.log(value[0]); // 42
console.log(value[1]); // 43
console.log(value[2]); // 44
});

拒绝处理程序总是接受一个值而非数组,改值来自被拒绝Promise的拒绝值;

1
2
3
4
5
6
7
8
9
10
11
12
13
14
let p1 = new Promise(function(resolve, reject) {
resolve(42);
});
let p2 = new Promise(function(resolve, reject) {
reject(43);
});
let p3 = new Promise(function(resolve, reject) {
resolve(44);
});
let p4 = Promise.all([p1, p2, p3]);
p4.catch(function(value) {
console.log(Array.isArray(value)) // false
console.log(value); // 43
});

Promise.race

race方法监听多个Promise,只要有一个Promise被解决,整个race就被解决;如果选解决的是已完成Promise,则返回已完成Promise,如果被解决的是已拒绝Promise,返回的是已拒绝Promise:

1
2
3
4
5
6
7
8
9
10
11
let p1 = new Promise(function(resolve, reject) {
resolve(42);
});
let p2 = Promise.reject(43);
let p3 = new Promise(function(resolve, reject) {
resolve(44);
});
let p4 = Promise.race([p1, p2, p3]);
p4.catch(function(value) {
console.log(value); // 43
});