我不知道Promise
Promise实例的创建
- 使用new Promise();
- 使用类方法Promise.XXX(),如:Promise.resolve()、Promise.reject()、Promise.all()、Promise.race()等;
- 实例调用原型方法Promise.prototype.XXX(),如:.then()、.catch()、.finally();
- 上述方法都是立即得到一个promise对象;
Promise核心过程
1、通过构造方法创建promise实例
const executor = function(resolve, reject) {
...
}
let promise1 = new Promise(executor);
上面代码中,executor是我们定义的执行器函数,引擎通过new运算创建promise对象时,会先创建好promise对象的实例,并得到关联给该实例的两个置值器:resolve()和reject(),再调用executor,并把两个置值器作为入口参数传入并立即执行,之后返回promise实例。
这里executor的所有返回值都会被忽略,我们只能通过两个置值器来改变promise对象(代理/绑定)的值,可以传入任何除了该promise实例的值。如果传入了自身则:
let outResolve;
let promise1 = new Promise((resolve, reject) => {
outResolve = resolve;
});
outResolve(p); // TypeError: Chaining cycle detected for promise
2、没有延时
Promise机制中并没有时间维度,没有任何延时行为。在javascript中,不论使用原型方法、类方法、还是构造方法创建一个promise对象时,创建过程是立即完成的。只不过需要注意的是promise所代理的值是否就绪,如果就绪了,那么如promise1.then(func)
中的func函数就会被执行了。
3、Then链
在javascript中,"Then链"就是两个promise对象之间顺序执行的关系。如:
let promise1 = new Promise((resolve, reject) => {
resolve(100);
});
// 通过promise.then()来得到新的promise2
let promise2 = promise1.then(function foo() {
...
});
promise1.then()
代表了对顺序逻辑的理解,同时也说明promise2与promise1两者所代理的数据之间是有关联的。上例中foo()函数其实是onFulfilled参数:
// .then()方法,上例中的foo()函数作为onFulfilled参数传入
let promise2 = promise1.then(onFulfilled, onRejected);
function onFulfilled(value) {
...
}
function onRejected(reason) {
...
}
当promise1的数据就绪(promise1对象绑定了值)时,引擎就将根据就绪状态触发onFulfilled/onRejected之一,并以该函数:
-
退出时的返回值,或
-
中止执行时的状态(如异常)
作为值来调用promise2的置值器。
因此从宏观上来看,是给“promise1绑定值”的行为(result ready就绪)触发了thenable行为。所谓“thenable行为”,就是调用then链的后续promise置值器,并在整个链上触发连锁的“thenable行为”。
promise1.then()也就是Promise.prototype.then原型方法主要做了三件事:
- 创建新的promise2对象;
- 登记promise1与promise2之间的关系;
- 将onFulfilled、onRejected关联给promise2的resolve置值器,并确保promise1绑定的数据就绪时调用onFulfilled、onRejected;
4、Then链中promise2的置值逻辑
一个promise可能会被置入两种值:
-
resolve成功,则该值为有效值(value);
-
resolve失败(如异常)或主动reject,则该值用于记录原因(reason);
在构建promise的执行器中可以向resolve/reject传入任意javascript数据。
接下来如果promise1的Then链上有promise2,那么前面说的onFulfilled、onRejected就会被触发:
// 响应onFulfilled、onRejected的函数
const resolved = function(value){}
const rejected = function(reason){}
// Then链
let promise2 = promise1.then(resolved, rejected);
其中resolved/rejected响应的都是promise1的状态,它们只是promise2的置值前提。
向promise2置值逻辑伪代码:
// 伪代码(1)。对于代码:
// let promise2 = promise1.then(resolved, rejected);
// 来说,对象promise2将发生如下置值逻辑:
try{
// the <result> proxy by <promise1>
// result 是 promise1代理的值
if(isRejected(promise1)){ // 如果promise1的状态为rejected,调用resolved, 反之调用rejected
x2 = rejected(result); // call onRejected <result> as reason
}else{
x2 = resolved(result); // call onFulfilled <result> as value
}
resolve(x2); // 置值promise2
}catch(e){
reject(e); // 置值promise2
}
这里看到无论resolved/rejected返回何值,都调用promise2的resolve置值器绑定值。只有当"thenable行为执行异常时",才会调用promise2的reject置值器。
另外需注意"resolve a rejected promise"仍然会是一个reject值。所以,在Then链中,产生reject值的方法有两种,一种是如上抛异常;另一种是通过Promise.reject()显式的返回。如:
// 在Then链上reject一个promise
// 方法1
let promise2 = promise1.then(()=> {
throw new Error();
});
// 方法2
let promise2 = promise1.then(()=> {
return Promise.reject(reason)
})
这两种方法得到的reason不同:方法1得到一个错误对象作为reason值;方法2得到一个不确定类型的reason值来表示原因,它可以是任意javascript数据。
5、Then链对值的传递以及.catch()处理
有一种特殊情况:promise2的resolve没有关联有效的 onFulfilled、onRejected函数,或者根本就没有这些函数,那么仍会产生promise2实例,并且它的resolve将以Then链中当前promise(这里是promise1)的值为值,这又叫做值穿透。置值逻辑伪代码如下:
// 伪代码(2)。对于代码:
// let promise2 = promise1.then(resolved, rejected);
// 完整置值逻辑:
try{
// the <result> proxy by <promise1>
// result 是 promise1代理的值
if(isRejected(promise1)){
if(!isValidHandler(rejected)) throw result; // 将result做为reason
x2 = rejected(result);
}else{
x2 = isValidHandler(resolved) ? resolved(result) // 同 “伪代码(1)”
: result; // 直接用promise1代理的数据
}
resolve(x2); // 置值promise2
}catch(e){
reject(e); // 置值promise2
}
检测如下:
// 得到一个promise
let p = Promise.resolve(100);
// 通过Then链得到promise2,不传入onFulfilled、onRejected
let promise2 = p.then();
// 由于没有函数响应onFulfilled、onRejected,所以promise2将默认使用p所代理的值
promise2.then(console.log);
类似地,这一置值过程也解释了将.catch()用作Then链结尾的用法:
// 得到一个rejected的promise
let p = Promise.reject('reject reason');
// p是rejected的,p.then调用中并没有传入rejected响应函数
let promise2 = p.then(x=>x);
// promise3是promise2使用.catch()作为链尾得到的一个promise
let promise3 = promise2.catch(console.log); // 'reject reason'
所以在任意长的Then链中,如果链的前端出现了rejected值,无论经过多少级.then()响应(只响应onFulfilled),最终该rejected值都能持续向后传递并被.catch()响应到。这也是Promise机制在javascript中应用的第一原则:始于promise,终于catch。确保链上任何可能的异常都得以处理。