PromiseA+规范和应用
技术参考:promisesaplus.com/
在了解和实现promise规范和应用之前先了解下promise术语,以便后续提到的时候可以有明确统一的概念。
术语
- promise是一个有then方法的对象或者函数,其行为都应遵循本规范。
- thenable 是一个有then方法的对象和函数。
- value是promise状态成功时候的值,也就是resolve的参数,包括各种数据类型,也包括undefined、thenable,甚至是promise。
- reason是promise状态失败时候的值,也就是reject的参数,表示拒绝的原因。
- exception是一个使用throw抛出的异常值。
规范
接下来分几部分来讲解PromiseA+规范
Promise status
Promise应该有三个状态,注意他们之间的流转。
-
pending
- 初始状态,可改变。
- 一个Promise在resolve和reject之前都处于这个状态。
- 可以通过resolve => fulfilled 状态。
- 可以通过reject => rejected 状态。
-
fulfilled
- 最终态,不可修改。
- 一个Promise被resolve之后变成这个状态。
- 必须拥有一个value值。
-
rejected
- 最终态,不可修改。
- 一个Promise被rejecte之后变成这个状态。
- 必须拥有一个reason值。 Tips:总结一下Promise的流转是这样的
pending => resolve(value) => fulfilled
pending => rejecet(value) => rejected
then
Promise 应该提供一个.then方法,用来访问最终结果,无论是value还是reason。
Promise.then( onFulfilled , onRejected )
-
参数要求
- onFulfilled必须是函数类型,如果不是函数,应该被忽略。
- onRejected 必须是函数类型,如果不是函数,应该被忽略。
-
onFulfilled
- 在Promise变成fulfilled状态的时候,应该执行onFulfilled函数,参数是value
- 在Promise变成fulfilled之前,onFulfilled不应该被执行。
- 只能调用一次(所以在实现的时候需要一个变量来控制执行次数)
-
onRejected
- 在Promise变成rejected状态的时候,应该执行onRejected函数,参数是reason
- 在Promise变成rejected之前,onRejected不应该被执行。
- 只能调用一次(所以在实现的时候需要一个变量来控制执行次数)
-
onFulfilled和onRejected 应该是微任务,我们这里用queueMicrotask来实现微任务的调用
-
then方法可以被调用多次
- Promise在变成fulfilled状态后,所有的onFulfilled回调都应该需要按照then的执行顺序,也就是按照注册的顺序执行(所以在实现的时候需要定义一个数组来存放多个onFulfilled的回调)
- Promise在变成rejected状态后,所有的onRejected回调都应该需要按照then的执行顺序,也就是按照注册的顺序执行(所以在实现的时候需要定义一个数组来存放多个onRejected的回调)
-
返回值:then方法都应该返回一个Promise =>这里定义为 promise2
- onFulfilled和onRejected的执行结果为X,执行resolvePeomise(这里可能会有些难理解,可以先保留,下面会详细讲一下resolvePromise是什么。)
- onFulfilled或者onRejeted在执行时抛出异常e,promise2都需要被reject。
- 如果onFulfilled不是一个函数,promise2应该以promise1的value触发onFulfilled。
- 如果onRejected不是一个函数,promise2应该以promise1的reason触发onRejected。
-
resolvePromise
resolvePromise(Promise2, x, resolve, reject)-
如果Promise2 和x相等,那个reject TypeError
-
如果x是一个Promise:
(1) 如果x是pending 状态,那么Promise必须要在pending状态,知道状态修改为fulfilled或者rejected。
(2) 如果x是fulfilled状态,fulfill Promise the same value。
(3) 如果x是rejected状态,reject Promise the same reason。
-
如果x是一个object或者是一个function
let then = x.then
如果x.then这步出错,那么reject Promise the same reason。
如果then是一个函数,then.call(x,resolvePromiseFn,rejectPromiseFn)
resolvePromiseFn 的入参是y,执行resolvePromise(promise2, y, resolve, rejsect)
resolvePromise 的入参是r,reject promise with r。
如果rejectPromise 和 rejectPromise 都调用了那么第一个调用的优先,后面的调用忽略。
如果调用then方法抛出异常e:如果resolvePromise或者rejectPromise已经被调用,那么忽略,否则以e为reason拒绝Promise。
如果then不是一个function,以x为value执行(fulfill)执行promise
-
PromiseA+ 的实现
接下来我们用class实现一下promise
- 先定义一个class框架,以及构造函数constructor
class MPromise {
constructor() {}
}
- 定义三种状态类型
const PENDING = 'pending';
const FULFILLED = 'fulfilled';
const REJECTED = 'rejected';
- 设置初始状态
class MPromise {
constructor() {
// 初始状态为pending
this.status = PENDING;
this.value = null;
this.reason = null;
}
}
-
定义resolve和reject 方法
- 根据上面的规范, 这两个方法是要更改status的,从pending到fulfilled/rejected
- 注意这两个函数的入参分别是value和reason
class MPromise {
constructor() {
// 初始状态为pending
this.status = PENDING;
this.value = null;
this.reason = null;
}
resolve(value) { //入参为value
//当状态为pending时,修改状态为fulfilled,并修改value值
if (this.status === PENDING) {
this.value = value;
this.status = FULFILLED;
}
}
reject(reason) {
//当状态为pending时,修改状态为rejected,并修改reason值
if (this.status === PENDING) {
this.reason = reason;
this.status = REJECTED;
}
}
}
- 回想一下new Promise((resolve, reject) => {}) 一般会传入一个函数,我们的这里也要添加一个入参
fn:- 入参是一个函数,并接收resolve和reject两个参数。
- 注意初始化Promise的时候,就要执行这个函数,并且任何报错都要通过reject刨除去。Tips:这里是同步执行
class MPromise {
constructor(fn) {
// 初始状态为pending
this.status = PENDING;
this.value = null;
this.reason = null;
try {
//这里将this指向为当前this,不希望在执行的时候改变this指向
fn(this.resolve.bind(this), this.reject.bind(this));
} catch (e) {
this.reject(e);
}
}
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方法
- then方法接收两个参数,onFulfilled和onRejected
then(onFulfilled, onRejected){}- 检查并处理参数,之前提到的如果不是函数function,就忽略,这个忽略是指原样返回value或者reason。
// 定义一个工具函数:用来判断参数是不是函数function isFunction(param) { return typeof param === 'function'; } then(onFulfilled, onRejected) { //判断是不是函数,并用新的变量进行接收 const realOnFulfilled = this.isFunction(onFulfilled) ? onFulfilled : (value) => { return value } const realOnRejected = this.isFunction(onRejected) ? onRejected : (reason) => { throw reason; }; }- .then 方法的返回值整体是一个Promise,所以咱们先用Promise来包裹一下。
then(onFulfilled, onRejected) { const realOnFulfilled = this.isFunction(onFulfilled) ? onFulfilled : (value) => { return value } const realOnRejected = this.isFunction(onRejected) ? onRejected : (reason) => { throw reason; }; const promise2 = new MPromise((resolve, reject) => {}) return promise2 }- 根据当前Promise的状态调用不同的函数
then(onFulfilled, onRejected) { const realOnFulfilled = this.isFunction(onFulfilled) ? onFulfilled : (value) => { return value } const realOnRejected = this.isFunction(onRejected) ? onRejected : (reason) => { throw reason; }; //new Promise().then(.....) const promise2 = new MPromise((resolve, reject) => { switch (this.status) { case FULFILLED: { realOnFulfilled() break; } case REJECTED: { realOnRejected() break; } } }) return promise2 }-
有人可能会有疑问,这样写, 是在then函数被调用的瞬间就会执行. 那这时候如果status还没变成fulfilled或者rejected怎么办, 很有可能还是pending的. 所以我们需要一个状态的监听机制, 当状态变成fulfilled或者rejected后, 再去执行callback。
(1). 那么我们首先要拿到所有的callback, 然后才能在某个时机去执行他. 新建两个数组, 来分别存储成功和失败的回调, 调用then的时候, 如果还是pending就存入数组.
// 存储执行resolve 改变状态的数组 FULFILLED_CALLBACK_LIST = []; // 存储执行reject 改变状态的数组 REJECTED_CALLBACK_LIST = []; constructor(fn){ ... } then(onFulfilled, onRejected) { const realOnFulfilled = this.isFunction(onFulfilled) ? onFulfilled : (value) => { return value } const realOnRejected = this.isFunction(onRejected) ? onRejected : (reason) => { throw reason; }; const promise2 = new MPromise((resolve, reject) => { switch (this.status) { case FULFILLED: { realOnFulfilled() break; } case REJECTED: { realOnRejected() break; } case PENDING: { //pending 时候将回调函数存储 this.FULFILLED_CALLBACK_LIST.push(realOnFulfilled) this.REJECTED_CALLBACK_LIST.push(realOnRejected) } } }) return promise2 }(2). 在status发生变化的时候, 就执行所有的回调. 这里咱们用一下es6的getter和setter. 这样更符合语义, 当status改变时, 去做什么事情. (当然也可以顺序执行, 在给status赋值后, 下面再加一行forEach)
_status = PENDING; get status() { //监听status状态 return this._status; } set status(newStatus) { //重新赋值status this._status = newStatus; //根据监听到的状态进行处理 switch (newStatus) { case FULFILLED: { this.FULFILLED_CALLBACK_LIST.forEach(callback => { callback(this.value);//值得注意的是这里传入的参数是this.value }); break; } case REJECTED: { this.REJECTED_CALLBACK_LIST.forEach(callback => { callback(this.reason); }); break; } } } -
then的返回值: 上面只是简单说了下, then的返回值是一个Promise, 那么接下来具体讲一下返回promise的value和reason是什么.
(1). 如果 onFulfilled 或者 onRejected 抛出一个异常 e ,则 promise2 必须拒绝执行,并返回拒因 e。(这样的话, 我们就需要手动catch代码,遇到报错就reject)
then(onFulfilled, onRejected) { const realOnFulfilled = this.isFunction(onFulfilled) ? onFulfilled : (value) => { return value } const realOnRejected = this.isFunction(onRejected) ? onRejected : (reason) => { throw reason; }; const promise2 = new MPromise((resolve, reject) => { //生命新的变量进行处理 const fulfilledMicrotask = () => { //遇到异常就进行reject try { realOnFulfilled(this.value); } catch (e) { reject(e) } }; const rejectedMicrotask = () => { try { realOnRejected(this.reason); } catch (e) { reject(e); } } switch (this.status) { case FULFILLED: { fulfilledMicrotask() break; } case REJECTED: { rejectedMicrotask() break; } case PENDING: { //替换成新的任务变量 this.FULFILLED_CALLBACK_LIST.push(fulfilledMicrotask) this.REJECTED_CALLBACK_LIST.push(rejectedMicrotask) } } }) return promise2 }(2). 如果 onFulfilled 不是函数且 promise1 成功执行, promise2 必须成功执行并返回相同的值
(3). 如果 onRejected 不是函数且 promise1 拒绝执行, promise2 必须拒绝执行并返回相同的据因。 这里咱们其实已经在参数检查的时候做过了, 也就是这段代码
const realOnFulfilled = this.isFunction(onFulfilled) ? onFulfilled : (value) => { return value } const realOnRejected = this.isFunction(onRejected) ? onRejected : (reason) => { throw reason; };(4)如果 onFulfilled 或者 onRejected 返回一个值 x ,则运行resolvePromise方法
then(onFulfilled, onRejected) { const realOnFulfilled = this.isFunction(onFulfilled) ? onFulfilled : (value) => { return value } const realOnRejected = this.isFunction(onRejected) ? onRejected : (reason) => { throw reason; }; const promise2 = new MPromise((resolve, reject) => { const fulfilledMicrotask = () => { try { //onFulfilled执行之后的返回值 => x const x = realOnFulfilled(this.value); this.resolvePromise(promise2, x, resolve, reject); } catch (e) { reject(e) } }; const rejectedMicrotask = () => { try { const x = realOnRejected(this.reason); this.resolvePromise(promise2, x, resolve, reject); } catch (e) { reject(e); } } switch (this.status) { case FULFILLED: { fulfilledMicrotask() break; } case REJECTED: { rejectedMicrotask() break; } case PENDING: { this.FULFILLED_CALLBACK_LIST.push(fulfilledMicrotask) this.REJECTED_CALLBACK_LIST.push(rejectedMicrotask) } } }) return promise2 } -
resolvePromise
resolvePromise(promise2, x, resolve, reject) { // 如果 newPromise 和 x 指向同一对象,以 TypeError 为据因拒绝执行 newPromise // 这是为了防止死循环 if (promise2 === x) { return reject(new TypeError('The promise and the return value are the same')); } if (x instanceof MPromise) { // 如果 x 为 Promise ,则使 newPromise 接受 x 的状态 // 也就是继续执行x,如果执行的时候拿到一个y,还要继续解析y queueMicrotask(() => { x.then((y) => { this.resolvePromise(promise2, y, resolve, reject); }, reject); }) } else if (typeof x === 'object' || this.isFunction(x)) { // 如果 x 为对象或者函数 if (x === null) { // null也会被判断为对象 return resolve(x); } let then = null; try { // 把 x.then 赋值给 then then = x.then; } catch (error) { // 如果取 x.then 的值时抛出错误 e ,则以 e 为据因拒绝 promise return reject(error); } // 如果 then 是函数 if (this.isFunction(then)) { let called = false; // 将 x 作为函数的作用域 this 调用 // 传递两个回调函数作为参数,第一个参数叫做 resolvePromise ,第二个参数叫做ejectPromise try { then.call( x, // 如果 resolvePromise 以值 y 为参数被调用,则运行 resolvePromise (y) => { // 需要有一个变量called来保证只调用一次. if (called) return; called = true; this.resolvePromise(promise2, y, resolve, reject); }, // 如果 rejectPromise 以据因 r 为参数被调用,则以据因 r 拒绝 promise (r) => { if (called) return; called = true; reject(r); }); } catch (error) { // 如果调用 then 方法抛出了异常 e: if (called) return; // 否则以 e 为据因拒绝 promise reject(error); } } else { // 如果 then 不是函数,以 x 为参数执行 promise resolve(x); } } else { // 如果 x 不为对象或者函数,以 x 为参数执行 promise resolve(x); } } -
onFulfilled 和 onRejected 是微任务, 咱们可以用queueMicrotask包裹执行函数
const fulfilledMicrotask = () => { //创建微任务 queueMicrotask(() => { try { const x = realOnFulfilled(this.value); this.resolvePromise(promise2, x, resolve, reject); } catch (e) { reject(e) } }) }; const rejectedMicrotask = () => { queueMicrotask(() => { try { const x = realOnRejected(this.reason); this.resolvePromise(promise2, x, resolve, reject); } catch (e) { reject(e); } }) }- catch 方法
catch (onRejected) { return this.then(null, onRejected); }- resolve 方法:将现有对象转为Promise对象,如果 Promise.resolve 方法的参数,不是具有 then 方法的对象(又称 thenable 对象),则返回一个新的 Promise 对象,且它的状态为fulfilled。Tips:注意这是一个静态方法, 因为咱们是通过Promise.resolve调用的, 而不是通过实例去调用的.
static resolve(value) { if (value instanceof MPromise) { return value; } return new MPromise((resolve) => { resolve(value); }); }- reject方法:返回一个新的Promise实例,该实例的状态为rejected。Promise.reject方法的参数reason,会被传递给实例的回调函数。
static reject(reason) { return new MPromise((resolve, reject) => { reject(reason); }); }- race方法 :
const p = Promise.race([p1, p2, p3]);该方法是将多个 Promise 实例,包装成一个新的 Promise 实例。只要p1、p2、p3之中有一个实例率先改变状态,p的状态就跟着改变。那个率先改变的 Promise 实例的返回值,就传递给p的回调函数。
static race(promiseList) {
return new MPromise((resolve, reject) => {
const length = promiseList.length;
if (length === 0) {
return resolve();
} else {
for (let i = 0; i < length; i++) {
MPromise.resolve(promiseList[i]).then(
(value) => {
return resolve(value);
},
(reason) => {
return reject(reason);
});
}
}
});
}
- all方法 :接收一个promise数组,全部成功才返回,只要有个一抛出异常就reject
static
all(promiseList){
return new MPromise((resolve, reject)=>{
if(!Array.isArray(promiseList)){
reject('input param must be a list')
}
let size = promiseList.length
if(size === 0){
resolve()
}else{
let count = 0
let res = []
for(let i=0; i<size; i++){
MPromise.resolve(promiseList(i)).then((value)=>{
res[i] = value
if(count++ === size){
resolve(res)
}
}).catch((reason)=>{
reject(reason)
})
}
}
})
}
全代码
const PENDING = 'pending';
const FULFILLED = 'fulfilled';
const REJECTED = 'rejected';
class MPromise {
FULFILLED_CALLBACK_LIST = [];
REJECTED_CALLBACK_LIST = [];
_status = PENDING;
constructor(fn) {
// 初始状态为pending
this.status = PENDING;
this.value = null;
this.reason = null;
try {
fn(this.resolve.bind(this), this.reject.bind(this));
} catch (e) {
this.reject(e);
}
}
get status() {
return this._status;
}
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;
}
}
}
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 : (value) => {
return value
}
const realOnRejected = this.isFunction(onRejected) ? onRejected : (reason) => {
throw reason;
};
const promise2 = new MPromise((resolve, reject) => {
const fulfilledMicrotask = () => {
queueMicrotask(() => {
try {
const x = realOnFulfilled(this.value);
this.resolvePromise(promise2, x, resolve, reject);
} catch (e) {
reject(e)
}
})
};
const rejectedMicrotask = () => {
queueMicrotask(() => {
try {
const x = realOnRejected(this.reason);
this.resolvePromise(promise2, x, resolve, reject);
} catch (e) {
reject(e);
}
})
}
switch (this.status) {
case FULFILLED: {
fulfilledMicrotask()
break;
}
case REJECTED: {
rejectedMicrotask()
break;
}
case PENDING: {
this.FULFILLED_CALLBACK_LIST.push(fulfilledMicrotask)
this.REJECTED_CALLBACK_LIST.push(rejectedMicrotask)
}
}
})
return promise2
}
catch(onRejected) {
return this.then(null, onRejected);
}
isFunction(param) {
return typeof param === 'function';
}
resolvePromise(promise2, x, resolve, reject) {
// 如果 newPromise 和 x 指向同一对象,以 TypeError 为据因拒绝执行 newPromise
// 这是为了防止死循环
if (promise2 === x) {
return reject(new TypeError('The promise and the return value are the same'));
}
if (x instanceof MPromise) {
// 如果 x 为 Promise ,则使 newPromise 接受 x 的状态
// 也就是继续执行x,如果执行的时候拿到一个y,还要继续解析y
queueMicrotask(() => {
x.then((y) => {
this.resolvePromise(promise2, y, resolve, reject);
}, reject);
})
} else if (typeof x === 'object' || this.isFunction(x)) {
// 如果 x 为对象或者函数
if (x === null) {
// null也会被判断为对象
return resolve(x);
}
let then = null;
try {
// 把 x.then 赋值给 then
then = x.then;
} catch (error) {
// 如果取 x.then 的值时抛出错误 e ,则以 e 为据因拒绝 promise
return reject(error);
}
// 如果 then 是函数
if (this.isFunction(then)) {
let called = false;
// 将 x 作为函数的作用域 this 调用
// 传递两个回调函数作为参数,第一个参数叫做 resolvePromise ,第二个参数叫做 rejectPromise
try {
then.call(
x,
// 如果 resolvePromise 以值 y 为参数被调用,则运行 resolvePromise
(y) => {
// 需要有一个变量called来保证只调用一次.
if (called) return;
called = true;
this.resolvePromise(promise2, y, resolve, reject);
},
// 如果 rejectPromise 以据因 r 为参数被调用,则以据因 r 拒绝 promise
(r) => {
if (called) return;
called = true;
reject(r);
});
} catch (error) {
// 如果调用 then 方法抛出了异常 e:
if (called) return;
// 否则以 e 为据因拒绝 promise
reject(error);
}
} else {
// 如果 then 不是函数,以 x 为参数执行 promise
resolve(x);
}
} else {
// 如果 x 不为对象或者函数,以 x 为参数执行 promise
resolve(x);
}
}
static resolve(value) {
if (value instanceof MPromise) {
return value;
}
return new MPromise((resolve) => {
resolve(value);
});
}
static reject(reason) {
return new MPromise((resolve, reject) => {
reject(reason);
});
}
static race(promiseList) {
return new MPromise((resolve, reject) => {
const length = promiseList.length;
if (length === 0) {
return resolve();
} else {
for (let i = 0; i < length; i++) {
MPromise.resolve(promiseList[i]).then(
(value) => {
return resolve(value);
},
(reason) => {
return reject(reason);
});
}
}
});
}
static all(promiseList) {
return new MPromise((resolve, reject) => {
if (!Array.isArray(promiseList)) {
reject('input param must be a list')
}
let size = promiseList.length
if (size === 0) {
resolve()
} else {
let count = 0
let res = []
for (let i = 0; i < size; i++) {
MPromise.resolve(promiseList(i)).then((value) => {
res[i] = value
if (count++ === size) {
resolve(res)
}
}).catch((reason) => {
reject(reason)
})
}
}
})
}
}