在开发过程中,我们经常需要处理异步的情况,开发人员对异步的处理方式也从轮询到回调,再到现在的Promise。于是,我打算对Promise整理一番。
Promise类似于XMLHttpRequest,通过构造函数Promise来创建一个新Promise对象作为接口。要想创建一个promise对象,可以使用new来调用Promise的构造器实例化:
var promise=new Promise(function(resolve,reject){
//异步处理
//处理结束后,调用resolve或reject
})
Promise的状态
用new Promise实例化的对象有三个状态:
- Fulfilled:resolve(成功)时,此时会调用onFulfilled;
- Rejected:reject(失败)时,此时会调用onRejected;
- Pending:既不是resolve也不是reject的状态,也就是promise对象刚被创建后的初始化状态。
Promise.resolve
Promise.resolve(value)可以认为是以下代码的语法糖:
new Promise(function(resolve){
resolve(value);
});
在这段代码中的resolve(value);
会让这个promise对象立即进入确定(即resolved)状态,并将value传递给后面then里所指定的onFulfilled函数。
方法Promise.resolve(value);
的返回值也是一个promise对象,所以我们可以像下面那样接着对其返回值进行.then调用:
Promise.resolve(value).then(function(value){
console.log(value);
})
Promise.resolve
方法的另一个作用就是将thenable对象转换为promise对象。
thenable,简单来说是一个非常类似于promise的东西,它指一个具有
.then
方法的对象。例如jQuery.ajax(),它的返回值是一个具有.then方法的jqXHR Object对象,这个对象继承了来自Deferred Object的方法和属性。但是Deferred Object并没有遵循Promises/A+或ES6 Promises标准,所以即使看上去这个对象转换成了一个promise对象,但是二者的then方法机制不同。
将thenable对象转换成promise对象:
var promise=Promise.resolve($.ajax(...));
promise.then(function(value){
console.log(value);
});
简单总结一下Promise.resolve
方法的话,可以认为它的作用就是将传递给它的参数填充(Fulfilled)到promise对象后并返回这个promise对象,它的参数主要分为以下三类:
- 接收到promise对象参数时:返回该promise对象;
- 接收到thenable类型的对象时:返回一个新的promise对象,该对象具有一个then方法;
- 接收到其他类型的参数(或者参数为空)时:返回一个将该参数作为值的新promise对象
另外,调用resolve或reject并不会终结Promise的参数函数的执行:
var promise = new Promise(function (resolve){
console.log("promise1");
resolve(42);
console.log("promise2");
});
promise.then(function(value){
console.log(value);
});
结果:上面代码中,虽然在调用了 resolve(42)
之后打印promise2,但是实际上promise2先打印出来。
Promise.reject
Promise.reject(error)可以认为是以下代码的语法糖:
new Promise(function(resolve,reject){
reject(new Error("出错了"));
});
Promise只能进行异步操作?
promise是同步的,.then是异步的(规定:Promise只能使用异步调用方式):
var promise = new Promise(function (resolve){
console.log("inner promise");
resolve(42);
});
promise.then(function(value){
console.log(value);
});
console.log("outer promise");
结果:依照结果来看,打印出"inner promise"后紧接着打印出了"outer promise",最后才调用.then方法打印出value。所以为了避免同步调用和异步调用同时存在导致的混乱,即使.then方法可以立即调用(即同步调用),Promise也会以异步的方式调用该回调函数,这是在Promise设计上的规定方针。
所以这个方针也告诉我们,如果我们的回调函数可能会同步调用,也可能异步调用,最好选择同一使用异步调用的方式。例如,将代码:
function onReady(fn) {
var readyState = document.readyState;
if (readyState === 'interactive' || readyState === 'complete') {
fn(); // 注意这里
} else {
window.addEventListener('DOMContentLoaded', fn);
}
}
onReady(function () {
console.log('DOM fully loaded and parsed');
});
console.log('==Starting==');
中的fn改为:
setTimeout(fn, 0);
这样,同步的fn回调就变为了异步的fn回调。
Promise.all
Promise.all接收一个promise对象的数组作为参数,当这个数组里的所有promise对象全部变为resolve或reject状态时,它才会去调用.then方法,并且.then方法中的的执行结果的参数顺序与all()中各promise对象的顺序一致。
举例:
var promise1=ajax({...});
var promise2=ajax({...});
Promise.all([promise1,promise2]).then(fn);
只有promise1和promise2都完成了,才会去调用then方法。
注意点:传递给Promise.all的promise并不是一个个顺序执行的,而是同时开始、并行执行的。对于上面的例子,promise1和promise2同时开始、并行执行。
Promise.race
Promise.race接收一个promise对象的数组作为参数,当这个数组里的所有promise对象中只要有一项变为resolve或reject状态时,它就会去调用.then方法,并且.then方法中的的执行结果的参数即为该promise对象的回调参数。
举例:
var promise1=ajax({...});
var promise2=ajax({...});
Promise.race([promise1,promise2]).then(fn);
只要promise1和promise2其中有一个完成,就会去调用then方法。
注意点:当Promise.race中的第一个promise对象变为确定(fulfilled)状态后,它之后的promise对象会继续运行,不会取消或者中断。对于上面的例子,只要promise1或者promise2其中某一个变为确定态,就会去调用then方法。
其他注意点
.catch是语法糖,
promise.catch(function(error){...})
等价于promise.then(undefined,function(error){...})
;每次调用then都会返回一个新创建的promise对象
使用
promise.then(onFulfilled, onRejected)
时,如果onFulfilled中发生异常,在onRejected中是捕获不到这个异常的;而在promise.then(onFulfilled).catch(onRejected)
中,.then中的异常能在.catch中捕获。使用reject而非throw
由于个人水平有限,博客错误之处,烦请指正!
参考资料:
1、JavaScript Promise迷你书
2、Promise/A+规范