Promise
对象代表着一个还未完成,但预期将来会完成的操作,该新特性属于 ECMAScript 2015(ES6)规范。
使用 Promise
对象,可以十分方便的摆脱层层嵌套的异步操作回调函数,而以同步操作的流程书写代码。
Promise
对象主要具有以下两个特点:
Promise
对象的状态只受构造函数内的同步或者一步操作影响,其他外部操作无法改变Promise
对象的状态。Promise
对象的状态改变,只会从pending
变为resolved
或者从pending
变为rejected
,状态改变后不再会发生改变。
浏览器兼容性
|Chrome |Edge |Firefox |IE |Opera |Safari|
|32.0 |Yes |29.0 |未实现 |19 |7.1|
Promise Constructor
Promise
对象构造器接受一个带有 resolve
和 reject
两个参数的函数,resolve
用于处理执行成功的场景,reject
用于处理执行失败的场景,我们可以根据执行结果决定调用哪个函数处理。
new Promise(function(resolve, reject) { … });
Promise对象有以下几种状态:
- pending: 初始状态;
- resolved: 成功的操作,又称fulfilled;
- rejected: 失败的操作。
示例如下:
var promiseCount = 0;
function testPromise() {
var thisPromiseCount = ++promiseCount;
console.log(thisPromiseCount + ') 开始(同步代码开始)');
// 我们创建一个新的promise: 然后用'result'字符串完成这个promise (3秒后)
var p1 = new Promise(function (resolve, reject) {
// 完成函数带着完成(resolve)或拒绝(reject)promise的能力被执行
console.log(thisPromiseCount + ') Promise开始(异步代码开始)');
console.log('//等待若干秒');
// 这只是个创建异步完成的示例
window.setTimeout(function () {
// 我们满足(fullfil)了这个promise!
resolve(thisPromiseCount)
}, Math.random() * 2000 + 1000);
});
// 定义当promise被满足时应做什么
p1.then(function (val) {
// 输出一段信息和一个值
console.log(val + ') Promise被满足了(异步代码结束)');
});
console.log(thisPromiseCount + ') 建立了Promise(同步代码结束)');
}
testPromise();
testPromise();
testPromise();
输出:
1) 开始(同步代码开始)
1) Promise开始(异步代码开始)
//等待若干秒
1) 建立了Promise(同步代码结束)
2) 开始(同步代码开始)
2) Promise开始(异步代码开始)
//等待若干秒
2) 建立了Promise(同步代码结束)
3) 开始(同步代码开始)
3) Promise开始(异步代码开始)
//等待若干秒
3) 建立了Promise(同步代码结束)
Promise 对象和其原型对象定义几个非常有用的方法:
Promise.prototype.then
then()
方法接收两个函数作为参数,并分别作为 success 和 failure 状态的回调函数,then() 方法返回一个 Promise 对象。
语法:
p.then(function(succData){}, function(errData){})
p.then(function(){})
then() 的第二个参数通常省略,因为无法捕获当前then() 函数的异常,而用统一的 catch() 函数进行异常处理,下节详述
then() 使用:
then() 方法返回 Promise 对象,因此可以链式调用,而前一个 then() 函数的返回值将会被作为参数传递给下一个 then() 函数:
var p = new Promise(function(resolve, reject) {
resolve("SUCCESS");
//or
//reject("FAILURE");
});
p.then(function(succData){
console.log(succData);//success
}, function(errData){
console.log(errData);//failure
});
链式调用:
then() 方法返回 Promise 对象,因此可以链式调用,而前一个 then() 函数的返回值将会被作为参数传递给下一个 then() 函数:
var p = new Promise(function(resolve, reject) {
resolve(1);
});
p.then(function(val) {
console.log(val); // 1
return val + 1;
}).then(function(val) {
console.log(val); // 2
}).then(function(val) {
console.log(val); // 3
});
Promise.prototype.catch
catch()
函数处理 Promise 失败的情形,即在 Promise 中调用了 reject(data) 的情形, 也可以捕获 then() 函数中抛出的错误。
语法:
p.catch(function(errData){})
catch使用
var p = new Promise(function(resolve, reject) {
//resolve("SUCCESS");
//or
reject("FAILURE");
});
p.then(function(succData){
console.log(succData);//success
}).catch(function(errData){
console.log(errData);//failure
});
//实际上等同于
p.then(function(succData){
console.log(succData);//success
}.then(null, function(errData){
console.log(errData);//failure
});
捕获 Promise 的错误
catch
也能捕获 promise语句中的错误,这种情况类似于在 promise 中调用 reject。
var p = new Promise(function(resolve, reject) {
throw("FAILURE");
});
p.then(function(succData){
throw new Error('FAILURE'); //不执行
}).catch(function(errData){
console.log(errData);//FAILURE
});
捕获 then 函数的错误
var p = new Promise(function(resolve, reject) {
resolve("SUCCESS");
});
p.then(function(succData){
throw new Error('FAILURE');
}).catch(function(errData){
console.log(errData);//FAILURE
});
catch 也支持链式调用
Promise 的错误总是向后抛给 catch 处理的,而 catch 的返回值也是 Promise 因此后一个 catch() 可以处理前一个 catch() 中抛出的错误
建议统一使用 catch 处理异常
尽管通过 then() 方法传递第二个参数可以处理异常,但是无法处理此个 then() 内的异常:
var p = new Promise(function(resolve, reject) {
reject(FAILURE1);
});
p.catch(function(errData){
console.log(errData);//FAILURE1
throw new Error('FAILURE2');
}).catch(function(errData){
console.log(errData);//FAILURE2
});
Promise.all
Promise.all
方法是 Promise 的一个静态方法,将多个 promise 实例(p1, p2, p3)包装成一个新的 promise 实例(p),此时会有两种执行结果:
1. p 会等所有 promise( p1, p2, p3 )都被 resolve 后 resolve,此时将这些 promise ( p1, p2, p3 )的返回值组成一个数组([p1,p2,p3]),传递给 p 的回调函数;
2. 如果 p1, p2, p3 中有一个被 reject 则该 reject ,并将该 reject的返回值传递给 p 的回调函数 。
语法:
Promise.all(iterable)
iterable
是一个可迭代对象,比如 Array,iterable 元素可以不是 promise 如果不是 promise 则会使用 Promise.resolve
转换为 Promise。
从多个数据源获取数据
下面是使用 ajax 从多个不同的接口获取数据的示例,如果所有的数据都获取成功,就可以渲染页面,否则报错。
p = Promise.race([
fetch('/resource/data1.json'),
fetch('/resource/data2.json'),
fetch('/resource/data3.json'),
]).then(([d1, d2, d3]) => {
//render page
}).catch(err => {
//err handler
});
Promise.race
Promise.race
方法是 Promise 的一个静态方法,将多个 promise 实例包装成一个新的 promise 实例,和 Promise.all
不同的是,这多个 promise 实例中的任意一个 promise 被解决或拒绝后,立刻以该 promise 的状态和传递的值返回给其回调函数,方法返回一个 Promise。
语法:
Promise.race(iterable) //iterable:一个可迭代对象,比如Array。
处理 ajax 异常状态
下面是使用 ajax 获取数据的示例,第二个 promise 在5s后状态会变为 reject ,如果 fetch 在5s 内获取导数据将会正常执行,否则会触发超时错误。
p = Promise.race([
fetch('/resource/data.json'),
new Promise(function (resolve, reject) {
setTimeout(() => reject(new Error('request timeout')), 5000)
})
]).then(response => {
//use data do sth
}).catch(error => {
//err handler
})
Promise.resolve
Promise.resolve
方法将给定的参数转换为一个 Promise 对象,
语法:
Promise.resolve(value);
Promise.resolve(promise);
Promise.resolve(thenable);
参数是 promise 对象
参数是 promise 对象,则不做操作直接返回这个 promise 对象。
Promise.resolve(Promise.resolve('Hello, world')).then(data => console.log(data));
参数是 thenable 对象
如果参数是一个包含 then
方法的对象,resolve
方法会将这个对象转为 Promise 对象,然后执行这个对象的 then
方法。
Promise.resolve({
then(resolve, reject) {
resolve('Hello, world');
}
}).then(data => console.log(data));//Hello, world
参数不是 thenable 和 promise 对象
如果参数是一个数值,或者是一个非 thenable
或非 promise
的对象,Promise.resolve
方法返回一个状态为 Resolved
的 promise 对象,并把这个参数作为返回值传递给 then
回调函数(如果存在 then 的回调)。
Promise.resolve('Hello, world').then(data => console.log(data)); //Hello, world
Promise.reject
Promise.reject
方法将给定的参数转换为一个 Promise 对象,
语法:
Promise.reject(value);
Promise.reject(promise);
Promise.reject(thenable);
具体的使用情况与 Promise.resolve
类似。