学习笔记之Promise进阶:从A+规范到手动实现
文章目录
一、Promise A+ 规范
(一)相关概念
-
promise:一个具有then方法的对象/函数,其行为遵循 Promise A+ 规范;
-
thenable:具有then方法的对象/函数;
-
value:promise实例的状态为兑现/成功时的值,即 resolve 的参数,可为任意类型;
-
reason:promise实例的状态为拒绝/失败时的值,即 reject 的值,表示拒绝/失败的原因;
-
exception:异常值
(二)A+ 规范
- states
Promise实例的状态,共有三种: pending,fulfilled,rejected。
(1) pending:
-
初始状态,可以改变,在resolve / reject执行之前都是这个状态。
-
在resolve执行后从pending状态改变为fufilled;
-
在reject执行后从pending状态改变为rejected;
(2) fulfilled:
-
是一种最终状态,不可再发生改变;
-
当处于pending状态的promise在经过resolve之后,其状态会变为fulfilled;
-
必须有一个value值,一般为resolve传入的参数,若resolve没有传参,则value值为undefined;
(3)rejected:
-
也是一种最终状态,不可再发生改变;
-
当处于pending状态的promis经过reject后,其状态会变为rejected;
-
必须有一个reason值,一般为reject传入的参数,若未传参数,则reason值为undefined;
需要注意的是,promise的状态只能从pending状态转变为fulfilled或者rejected,不可逆转,也不会在fulfilled和rejected之间转变。因此,一旦promise的状态已经是fulfilled或者rejected,即使之后又经过了resolve或reject,promise的状态也不会再发生变化。
- then方法
根据 A+ 规范,promise应该提供一个then方法,接收两个参数,用于访问最终状态的结果。
const promise = new Promise((resolve,reject)=>{
// ...
})
promise.then(onFulfilled, onRejected)
-
then方法的参数:onFulfilled应该是一个function,如果不是,则 onFulfilled 应该被忽略,而使用内部默认的function来替代它。onRejected同理。
-
onFulfilled:在promise的状态变为fulfilled后,应该调用onFulfilled,此时,onFulfilled的参数是value;而在promise状态变为fulfilled之前,则不应该调用此函数。此外,promise的状态只会发生一次变化,相对应的,一个onFulfilled也只能调用一次。
-
onRejected:在promise的状态变为rejected后,应该调用onRejected,此时,onRejected的参数是reason;而在promise状态变为rejected之前,则不应该调用此函数。此外,promise的状态只会发生一次变化,相对应的,一个onRejected也只能调用一次。
-
onFulfilled 和 onRejected 的执行环境是在微任务里。可使用queueMicrotask( )来将其加入微任务队列,不使用setTimeout的原因是:setTimeout为宏任务,不符合 A+ 规范。
-
then方法可以多次调用。在 promise 的状态变为 fulfilled 后,其所有的 onFulfilled 回调按照then的顺序执行;在 promise 的状态变为rejected后,其所有的 onRejected 回调按照then的顺序执行;
const promise = new Promise((resolve,reject)=>{ // ... }); // 可多次调用then方法,其中回调函数按照then的顺序 promise.then(onFulfilled, onRejected); promise.then(onFulfilled, onRejected); promise.then(onFulfilled, onRejected); promise.then(onFulfilled, onRejected);
-
then方法的返回值
then方法的返回值应该是一个船新的promise。
const promise1 = new Promise((resolve,reject)=>{ // ... }); // then方法返回一个新的promise,与 const promise2 = promise.then(onFulfilled, onRejected); // promise2 同样可以调用then方法 promise2.then(callback1, callback2)
-
根据 onFilfilled 或者onRejected 执行的结果,假设为 a,调用resolvePromise( )来解析promise;
-
当 onFulfilled 或 onRejected 执行时报错了,则 promise2 就需要被 reject ;
-
若 promise1 的 then 方法中,onFulfilled 不是一个function,则会调用内部的默认函数,使 promise2 以 promise1 的 value 来触发 fulfilled 。
-
若 promise1 的 then 方法中,onRejected 不是一个function,则会调用内部的默认函数,使 promise2 以 promise1 的 reason 来触发 rejected。
-
-
resolvePromise( )
resolvePromise(promise2, a, resolve, reject)
接下来进行条件判断:
-
promise2 === a,reject Type error;
-
如果 a 是一个promise:
-
a的状态是pending,则promise2也会处于pending状态直至 a 的状态改变;
-
a的状态是fulfilled,则promise2也以相同的value触发fulfilled;
-
a的状态是rejected,则promise2以相同的reason触发rejected;
-
-
如果 a 是一个object或function:
尝试取 a 的then方法看是否出错:let then = a.then,若出错则把错误reject出去。取到then之后,判断then的类型,如果then是一个函数则通过call调用then:then.call(a),否则resolve(a);
-
如果 a 是其他类型,则 resolve (a);
-
二、如何实现一个Promise
1.定义状态类型
const PENDING = 'pending',
FULFILLED = 'fulfilled',
REJECTED = 'rejected';
2.初始化class
定义初始状态,value以及reason。
class MyPromise {
constructor() {
this.status = PENDING;
this.value = null;
this.reason = null;
}
}
3.resolve和reject
-
更改status,从pending变为fulfilled或rejected;
-
更新value或reason值;
class MyPromise { constructor() { // ... 初始化 } resolve(value){ if(this.status === PENDING){ this.value = value; this.status = FULFILLED; } } reject(reason){ if(this.status === PENDING){ this.reason = reason; this.status = REJECTED; } } }
4.构造函数入参
new MyPromise((resolve, reject)=>{})
-
入参为一个函数 resolver,resolver接收两个参数:resolve 和 reject;
-
执行new Promise时,就会同步执行这个函数,发生任何的错误就会被reject出去。
class MyPromise {
constructor(resolver) {
this.status = PENDING;
this.value = null;
this.reason = null;
if(typeof resolver === 'function'){
// resolver是函数则执行resolver
// 错误捕获
try {
resolver(this.resolve.bind(this), this.reject.bind(this));
} catch(e){
this.reject(e)
}
}else{
// resolver不是函数则抛出错误
throw new Error(`Promise resolver ${resolver} is not a function`);
}
}
resolve(value){
// ...
};
reject(reason){
// ...
};
}
5.then方法
-
入参:onFulfilled,onRejected
-
返回值:新的promise
-
需要配判断入参是否为函数,如果不是则调用默认函数传递value和reason
先来看看下面这个写法。
class MyPromise {
constructor(resolver){
// ... 初始化、执行resolver
}
resolve(value){
// ...
}
reject(reason){
// ...
}
then(onFulfilled, onRejected){
const realOnFulfilled = isFunction(onFulfilled) ? onFulfilled
: (vaule) => value;
const realOnRejected = isFunction(onRejected) ? onRejected
: (reason) => {
throw reason
};
const promise2 = new MyPromise((resolve, reject)=>{
switch(this.status){
case FULFILLED:
realOnFulfilled();
break;
case REJECTED:
realOnRejected();
break;
}
});
return promise2
}
isFunction(func){
return typeof func === 'function'
}
}
这个写法存在问题,只能处理同步操作,一旦有异步执行resolve或reject,则调用then方法时,status仍为pending。另外,then方法可以执行多次,因此,需要两个队列来存储realOnFulfilled 和 realOnrejected,一旦status状态为pending,则将realOnFulfilled和realOnRejected添加进队列里,以便后续status值发生变化时依次调用。因此做如下改进:
-
增加两个队列FULFILLED_CALLBACK_LIST和REJECTED_CALLBACK_LIST分别在pending状态时存放 realOnFulfilled 和 realOnRejected;
-
在合适的时机调用队列里的回调函数,有两种方案:
-
在resolve和reject里,当status变为fulfilled或rejected时调用相应队列里的函数;
-
通过存取器 setter 来监听 status,一旦 status 发生变化,则一次调用相应队列里的处理程序。
这里我选择了后者,能让代码结构更清晰。如果在resolve和reject里调用,会增加代码的复杂性和混乱程度。
-
-
此外,根据 A+ 规范,当realOnFulfilled或realOnRejected为微任务环境,执行出错时,需要将错误reject出去,触发promise2的rejected。且根据其执行得到的结果 a 的不同,会在resolvePromise中有不同的操作。因此使用queueMicrotask( )将其放入微任务队列,并封装到fulfilledMicroTask和rejectedMicroTask中进行错误捕获。
class MyPromise {
// 添加队列
FULFILLED_CALLBACK_LIST = [];
REJECTED_CALLBACK_LIST = [];
// 因为使用了存取器,增加一个私有变量_status来储存真正的status的值
_status = PENDING
constructor(resolver){
// ... 初始化、执行resolver
}
// 通过存取器setter来监听status
// 需要一个私有变量_status来储存真正的status的值,否则在getter中会死循环
set status(newStatus){
this._status = newStatus;
switch(newStatus){
case FULFILLED:
this.FULFILLED_CALLBACK_LIST.forEach((callback)=>callback(this.value))
break;
case REJECTED:
this.REJECTED_CALLBACK_LIST.forEach((callback)=>callback(this.reason))
break;
}
}
get status(){
return this._status
}
resolve(value){
// ...
}
reject(reason){
// ...
}
then(onFulfilled, onRejected){
const realOnFulfilled = this.isFunction(onFulfilled) ? onFulfilled
: (vaule) => value;
const realOnRejected = this.isFunction(onRejected) ? onRejected
: (reason) => {
throw reason;
};
const promise2 = new MyPromise((resolve, reject)=>{
// 捕获realOnFulfilled执行过程中的错误并reject出去
const fulfilledMicroTask = () => {
queueMicrotask(() => {
try {
const a = realOnFulfilled(this.value);
this.resolvePromise(promise2, a, resolve, reject);
} catch(e){
reject(e);
}
})
}
// 捕获realOnRejected执行过程中的错误并reject出去
const rejectedMicroTask = () => {
queueMicrotask(() => {
try {
const a = realOnRejected(this.reason);
this.resolvePromise(promise2, a, resolve, reject);
} catch(e) {
reject(e);
}
})
}
switch(this.status){
// 成功时调用realOnFulfilled
case FULFILLED:
fulfilledMicroTask();
break;
// 拒绝时调用realOnRejected
case REJECTED:
rejectedMicroTask();
break;
// 等待时将其放入队列
case PENDING:
this.FULFILLED_CALLBACK_LIST.push(fulfilledMicroTask);
this.REJECTED_CALLBACK_LIST.push(rejectedMicroTask);
}
});
return promise2;
}
resolvePromise(promise2, a, resolve, reject){
// 根据a的值进行不同的操作
}
isFunction(func){
return typeof func === 'function';
}
}
接下来实现resolvePromise来解析 a 的结果。
// class MyPromise
// ...
resolvePromise(promise2, a, resolve, reject){
if(promise2 === a){
return reject(new TypeError('The promise and the return value are the same'));
}
if(a instanceof MyPromise){
queueMicroTask(()=>{
a.then(
(res) => {
this.resolvePromise(promise2, res, resolve, reject);
},
reject
)
})
}else if(typeof a === 'object' || typeof a === 'function'){
if(a === null){
return resolve(a);
}
let then = null;
try {
then = a.then;
} catch(e) {
return reject(e)
}
if(this.isFunction(then)){
// 保证函数只能调用一次
let hasCalled = false;
try {
then.call(
x,
(res) => {
if(hasCalled) return;
hasCalled = true;
this.resolvePromise(promise2, res, resolve, reject)
}, (e) => {
if(hasCalled) return;
hasCalled = true;
reject(e)
}
)
} catch(e) {
if(hasCalled){
return
}
reject(e)
}
}else{
resolve(a)
}
}else{
resolve(a)
}
}
// ...
6. catch方法
-
catch实质上就是调用了then方法,给第一个参数传入null,返回一个新的promise,即promise2;
-
在catch方法未执行完之前,promise2的status将一直是pending;
-
catch方法执行过程中如果报错,将触发promise2的status变为rejected;
-
若catch方法执行完毕且没有报错,将触发promise2的status变为fulfilled;
-
详见resolvePromise方法。
// class MyPromise
// ...
catch(onRejected){
return this.then(null, onRejected)
}
// ...
7. Promise.resolve()
静态方法,用提供的参数创建一个rosolve过的promise。
class MyPromise {
// ...
static resolve(value){
// 只需要resolve,不需要reject
return new MyPromise((resolve)=>{
resolve(value)
})
}
// ...
}
### 8. Promise.reject()
与Promise.resolve同理。
class MyPromise {
// ...
static reject(reason){
return new MyPromise((resolve, reject)=>{
reject(reason)
})
}
// ...
}
至此,Promise的核心功能都已基本实现。还剩下Promise.all,Promise.race等静态方法,有空再研究。以上代码整合如下,如有不足或错误之处,还望不吝指出,感激不尽!
// 定义三种状态
const PENDING = 'pending',
FULFILLED = 'fulfilled',
REJECTED = 'rejected';
class MyPromise {
// 添加队列
FULFILLED_CALLBACK_LIST = [];
REJECTED_CALLBACK_LIST = [];
// 因为使用了存取器,增加一个私有变量_status来储存真正的status的值
_status = PENDING
constructor(resolver) {
this.status = PENDING;
this.value = null;
this.reason = null;
if(typeof resolver === 'function'){
// resolver是函数则执行resolver
// 错误捕获
try {
resolver(this.resolve.bind(this), this.reject.bind(this));
} catch(e){
this.reject(e)
}
}else{
// resolver不是函数则抛出错误
throw new Error(`Promise resolver ${resolver} is not a function`);
}
}
// 通过存取器setter来监听status
// 需要一个私有变量_status来储存真正的status的值,否则在getter中会死循环
set status(newStatus){
this._status = newStatus;
switch(newStatus){
case FULFILLED:
this.FULFILLED_CALLBACK_LIST.forEach((callback)=>callback(this.value))
break;
case REJECTED:
this.REJECTED_CALLBACK_LIST.forEach((callback)=>callback(this.reason))
break;
}
}
get status(){
return this._status
}
resolve(value){
if(this.status === PENDING){
this.value = value;
this.status = FULFILLED;
}
}
reject(reason){
if(this.status === PENDING){
this.reason = reason;
this.status = REJECTED;
}
}
then(onFulfilled, onRejected){
const realOnFulfilled = this.isFunction(onFulfilled) ? onFulfilled
: (vaule) => value;
const realOnRejected = this.isFunction(onRejected) ? onRejected
: (reason) => {
throw reason;
};
const promise2 = new MyPromise((resolve, reject)=>{
// 捕获realOnFulfilled执行过程中的错误并reject出去
const fulfilledMicroTask = () => {
queueMicrotask(() => {
try {
const a = realOnFulfilled(this.value);
this.resolvePromise(promise2, a, resolve, reject);
} catch(e){
reject(e);
}
})
}
// 捕获realOnRejected执行过程中的错误并reject出去
const rejectedMicroTask = () => {
queueMicrotask(() => {
try {
const a = realOnRejected(this.reason);
this.resolvePromise(promise2, a, resolve, reject);
} catch(e) {
reject(e);
}
})
}
switch(this.status){
// 成功时调用realOnFulfilled
case FULFILLED:
fulfilledMicroTask();
break;
// 拒绝时调用realOnRejected
case REJECTED:
rejectedMicroTask();
break;
// 等待时将其放入队列
case PENDING:
this.FULFILLED_CALLBACK_LIST.unshift(fulfilledMicroTask);
this.REJECTED_CALLBACK_LIST.unshift(rejectedMicroTask);
}
});
return promise2;
}
catch(onRejected){
return this.then(null, onRejected)
}
resolvePromise(promise2, a, resolve, reject){
if(promise2 === a){
return reject(new TypeError('The promise and the return value are the same'));
}
if(a instanceof MyPromise){
queueMicroTask(()=>{
a.then(
(res) => {
this.resolvePromise(promise2, res, resolve, reject);
},
reject
)
})
}else if(typeof a === 'object' || typeof a === 'function'){
if(a === null){
return resolve(a);
}
let then = null;
try {
then = a.then;
} catch(e) {
return reject(e)
}
if(this.isFunction(then)){
// 保证函数只能调用一次
let hasCalled = false;
try {
then.call(
x,
(res) => {
if(hasCalled) return;
hasCalled = true;
this.resolvePromise(promise2, res, resolve, reject)
}, (e) => {
if(hasCalled) return;
hasCalled = true;
reject(e)
}
)
} catch(e) {
if(hasCalled){
return
}
reject(e)
}
}else{
resolve(a)
}
}else{
resolve(a)
}
}
isFunction(func){
return typeof func === 'function';
}
// 静态方法Promise.resolve()
static resolve(value){
// 只需要resolve,不需要reject
return new MyPromise((resolve)=>{
resolve(value)
})
}
// 静态方法Promise.reject()
static reject(reason){
return new MyPromise((resolve, reject)=>{
reject(reason)
})
}
}