1.什么是Promise
Promise可以认为是一种用来解决异步处理的代码规范。常见的异步处理是使用回调函数,回调函数有两种模式,同步的回调和异步的回调。一般回调函数指的是异步的回调。
同步回调
function add(a, b, callback) { callback(a + b) }
console.log('before');
add(1, 2, result => console.log('Result: ' + result);
console.log('after');
输出结果为: before Result:3 after
异步回调
function addAsync(a, b, callback) {
setTimeout( () => callback(a + b), 1000);
}
console.log('before');
addAsync(1, 2, result => console.log('Result: ' + result));
console.log('after');
输出结果: before after Result: 3
然而回调函数有个著名的坑就是“callback hell”,比如:
doSomething1(function(value1) {
doSomething2(function(value2) {
doSomething3(function(value3) {
console.log("done! The values are: " + [value1, value2, value3].join(','));
})
})
})
为了等value1, value2, value3数据都准备好,必须要一层一层嵌套回调函数。如果一直嵌套下去,就形成了callback hell,不利于代码的阅读。
如果改用Promise的写法,只要写成如下方式就行。
doSomething1().then(function() {
return value1;
}).then(function(tempValue1) {
return [tempValue1, value2].join(',');
}).then(function(tempValue2) {
console.log("done! ", [tempValue2, value3].join(','));
});
可以注意到,Promise实际上是把回调函数从doSomething函数中提取到了后面的then方法里面,从而防止多重嵌套的问题。
2.Promise对象和状态
2.1 resolve & reject
Promise构造函数用来构造一个Promise对象,其中入参匿名函数中resolve和reject这两个也都是函数。如果resolve执行了,则触发promise.then中成功的回调函数;如果reject执行了,则触发promise.then中拒绝的回调函数。
var promise = new Promise(function(resolve, reject) {
// IF 如果符合预期条件,调用resolve
resolve('success');
// ELSE 如果不符合预期条件,调用reject
reject('failure')
})
2.2 Fulfilled & Rejected
Promise对象一开始的值是Pending准备状态。
执行了resolve()后,该Promise对象的状态值变为onFulfilled状态。
执行了reject()后,该Promise对象的状态值变为onRejected状态。
Promise对象的状态值一旦确定(onFulfilled或onRejected),就不会再改变。即不会从onFulfilled转为onRejected,或者从onRejected转为onFulfilled。
2.3 快捷方法
获取一个onFulfilled状态的Promise对象:
Promise.resolve(1);
// 等价于
new Promise((resolve) => resolve(1));
获取一个onRejected状态的Promise对象:
Promise.reject(new Error("BOOM"))
// 等价于
new Promise((resolve, reject)
=> reject(new Error("BOOM")));
3.异常捕获:then和catch
Promise的异常捕获有两种方式:
then匿名函数中的reject方法
catch方法
3.1 then中的reject方法捕获异常
这种方法只能捕获前一个Promise对象中的异常,即调用then函数的Promise对象中出现的异常。
var promise = Promise.resolve();
promise.then(function() {
throw new Error("BOOM!")
}).then(function (success) {
console.log(success);
}, function (error) {
// 捕捉的是第一个then返回的Promise对象的错误
console.log(error);
});
但该种方法无法捕捉当前Promise对象的异常,如:
var promise = Promise.resolve();
promise.then(function() {
return 'success';
}).then(function (success) {
console.log(success);
throw new Error("Another BOOM!");
}, function (error) {
console.log(error); // 无法捕捉当前then中抛出的异常
});
3.2 catch捕获异常
上述栗子若改写成如下形式,最后追加一个catch函数,则可以正常捕捉到异常。
var promise = Promise.resolve();
promise.then(function() {
return 'success';
}).then(function (success) {
console.log(success);
throw new Error("Another BOOM!");
}).catch(function (error) {
console.log(error); // 可以正常捕捉到异常
});
catch方法可以捕获到then中抛出的错误,也能捕获前面Promise抛出的错误。 因此建议都通过catch方法捕捉异常。
var promise = Promise.reject("BOOM!");
promise.then(function() {
return 'success';
}).then(function (success) {
console.log(success);
throw new Error("Another BOOM!");
}).catch(function (error) {
console.log(error); // BOOM!
});
值得注意的是:catch方法其实等价于then(null, reject),上面可以写成:
promise.then(function() {
return 'success';
}).then(function (success) {
console.log(success);
throw new Error("Another BOOM!");
}).then(null, function(error) {
console.log(error);
})
总结来说就是:
- 使用promise.then(onFulfilled, onRejected)的话,在 onFulfilled中发生异常的话,在onRejected中是捕获不到这个异常的。
- 在promise.then(onFulfilled).catch(onRejected)的情况下then中产生的异常能在.catch中捕获
- .then和 .catch在本质上是没有区别的需要分场合使用。