手写参考PromiseA+规范
都已经9021年了,你该学会手写一个Promise了,快来看看吧。
手写基础的Promise
第一步 初写简单的Promise
tips: 我们可以先用
typescript来看一下原生的Promise方法
看一下PromiseA+规范
1.3 “value” is any legal JavaScript value (including
undefined, a thenable, or a promise).
1.4 “exception” is a value that is thrown using the
throwstatement.
1.5 “reason” is a value that indicates why a promise was rejected.
2.1 A promise must be in one of three states: pending, fulfilled, or rejected.
2.2 A promise must provide athenmethod to access its current or eventual value or reason.
A promise’sthenmethod accepts two arguments:
promise.then(onFulfilled, onRejected)
通过上述,我们能发现
- 每个
Promise都有三个状态:pending表示为等待态、fulfilled表示为成功态、rejected表示变为失败态,并且状态不可逆 - 原生的
Promise使用new方法使用的,说明它是一个类(也可以写成函数) - 它传入了一个
executor,它包括了两个参数:resolve,reject - 每个
Promise都有一个then方法,有两个可选参数,一个成功的回调onFulfilled,一个失败的回调onRejected。 - 当
Promise抛出异常后,走的是失败态
// 枚举三个状态
const enum STATUS {
pending = 'PENDING',
fulfilled = 'FULFILLED',
rejected = 'REJECTED'
}
class Promise {
status: STATUS
value: any
reason: any
constructor(executor:any){
this.status = STATUS.pending; // 当前默认状态
this.value = undefined; // 成功原因
this.reason = undefined; // 失败原因
const resolve = (value?:any) =>{
if(this.status === STATUS.pending){
this.status = STATUS.fulfilled;
this.value = value;
}
}
const reject = (reason?:any) =>{
if(this.status === STATUS.pending){
this.status = STATUS.rejected;
this.reason = reason;
}
}
try{
executor(resolve,reject);
}catch(e){
// 当抛出异常时,走reject
reject(e);
}
}
// 每个 Promise 都有一个 then 方法,有两个可选参数一个成功的回调,一个失败的回调。
then(onFulfilled?:any,onRejected?:any){
if(this.status == STATUS.fufilled){
onFulfilled(this.value);
}
if(this.status == STATUS.rejected){
onRejected(this.reason);
}
}
}
第二步 then方法的改造
当代码改变下
let promise = new Promise((resolve,rejected)=>{
setTimeout(()=>{
resolve('ok')
},1000)
})
promise.then((data)=>{
console.log('ok')
},(err)=>{
console.log('err')
})
我们发现不执行了,因为执行then的时候状态还是pending,所以不执行了。
我们再去看一下规范
2.2.6thenmay be called multiple times on the same promise.
2.2.6.1 If/whenpromiseis fulfilled, all respectiveonFulfilledcallbacks must execute in the order of their originating calls tothen.
2.2.6.2 If/whenpromiseis rejected, all respectiveonRejectedcallbacks must execute in the order of their originating calls tothen.
从文档中我们得知then可以被多次调用,并且如果是fulfilled会按顺序一个一个调用onFulfilled,如果是rejected会按顺序一个一个调用onRejected。这不就是之前说过的发布订阅的模式,先把所有的then方法按照onFulfilled,onRejected存为两个数组,然后最后依次执行。我们就改造一下我们之前的代码
class Promise {
status: STATUS
value: any
reason: any
onFulfilledCallbacks:Function[]
onRejectedCallbacks:Function[]
constructor(executor:resolve(value?:any)=>void,reject:(reason?:any)=>void){
this.status = STATUS.pending;
this.value = undefined;
this.reason = undefined;
this.onFulfilledCallbacks = [];
this.onRejectedCallbacks = []
const resolve = (value?:any) =>{
if(this.status === STATUS.pending){
this.status = STATUS.fulfilled;
this.value = value;
this.onFulfilledCallbacks.forEach(fn=>fn());
}
}
const reject = (reason?:any) =>{
if(this.status === STATUS.pending){
this.status = STATUS.rejected;
this.reason = reason;
this.onRejectedCallbacks.forEach(fn=>fn());
}
}
try{
executor(resolve,reject);
}catch(e){
reject(e);
}
}
then(onFulfilled?:any,onRejected?:any){
if(this.status == STATUS.fulfilled){
onFulfilled(this.value);
}
if(this.status == STATUS.rejected){
onRejected(this.reason);
}
if(this.status == STATUS.pending){
this.onFulfilledCallbacks.push(onFulfilled(this.value));
this.onRejectedCallbacks.push(onRejected(this.reason));
}
}
}
继续看文档
2.2.1 BothonFulfilledandonRejectedare optional arguments:
2.2.7thenmust return a promise
then的两个参数是可选参数,then只能每次生成一个新的promise。就是then的时候不能返回this,如果返回this,比如第一个return失败状态,而第二个失败态里面返回了一个成功状态,这样状态发生变化,但是我们规定状态不可逆。
然后我们根据规范改造一下then方法,别忘记出错要走reject方法
then(onFulfilled?:any,onRejected?:any){
onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : val => val;
onRejected = typeof onRejected === 'function' ? onRejected : err => { throw err };
// 每次调用then都产生一个全新的Promise
let promise2 = new Promise((resolve?:any,reject?:any)=>{
if(this.status == STATUS.fulfilled){
try{
let x = onFulfilled(this.value);
resolve(x);
}catch(e){
reject(e);
}
}
if(this.status == STATUS.rejected){
try{
let x = onRejected(this.reason);
resolve(x);
}catch(e){
reject(e)
}
}
if(this.status == STATUS.pending){
this.onFulfilledCallbacks.push(()=>{
// 切片编程 可以做额外的逻辑
try{
let x = onFulfilled(this.value);
resolve(x);
}catch(e){
reject(e);
}
});
this.onRejectedCallbacks.push(()=>{
try{
let x = onRejected(this.reason);
resolve(x);
}catch(e){
reject(e);
}
});
}
})
return promise2;
}
// 有另一种模式
const p={}
p.promise = new Promise((resolve?:any,reject?:any)=>{
p.resolve = resolve;
p.reject = reject
})
p.resolve(x);
第三步 当传参变化时
继续来个栗子
let promise = new Promise((resolve,rejected)=>{
resolve('ok')
}).then(data=>{
return new Promise((resolve,rejected)=>{
setTimeout(()=>{
resolve('ok');
},1000)
})
promise.then((data)=>{
console.log('ok')
},(err)=>{
console.log('err')
})
我们发现之前写的方法又报错了,因为返回值x的类型变了,变成了一个Promise。所以我们要对x的类型做一个判断。具体方法还是看文档。
2.2.7.1 If either
onFulfilledoronRejectedreturns a valuex, run the Promise Resolution Procedure[[Resolve]](promise2, x)
[2.2.4]onFulfilledoronRejectedmust not be called until the execution context stack contains only platform code. [3.1].
解析:文档我们要走一个叫做Resolve的方法他有两个参数promise2、x,通过它来判断x的属性是否是promise,并且对它进一步的处理,所以我们通过Resolve方法来替换掉以前的resolve(x)。并且onFulfilled、onRejected必须不能调用在当前执行上下文栈中,就是说我们要用微任务来处理下这两个方法,我们这里不讨论微任务,但是我们可以用setTimeout来代替一下。
then(onFulfilled?: any, onRejected?: any) {
// 每次调用then都产生一个全新的Promise
onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : val => val;
onRejected = typeof onRejected === 'function' ? onRejected : err => { throw err };
let promise2 = new Promise((resolve?: any, reject?: any) => {
if (this.status == STATUS.fulfilled) {
setTimeout(() => {
try {
let x = onFulfilled(this.value);
// 把以前的注释掉
// resolve(x);
// 也可以用
// promise2.resolve = resolve;
// promise2.reject=reject;
// resolvePromise(promise2,x);
resolvePromise(promise2, x, resolve, reject);
} catch (e) {
reject(e);
}
}, 0)
}
if (this.status == STATUS.rejected) {
setTimeout(() => {
try {
let x = onRejected(this.reason);
resolvePromise(promise2, x, resolve, reject);
} catch (e) {
reject(e)
}
}, 0)
}
if (this.status == STATUS.pending) {
this.onFulfilledCallbacks.push(() => {
// 切片编程 可以做额外的逻辑
setTimeout(() => {
try {
let x = onFulfilled(this.value);
resolvePromise(promise2, x, resolve, reject);
} catch (e) {
reject(e);
}
}, 0)
});
this.onRejectedCallbacks.push(() => {
setTimeout(() => {
try {
let x = onRejected(this.reason);
resolvePromise(promise2, x, resolve, reject);
} catch (e) {
reject(e);
}
}, 0)
});
}
})
return promise2;
}
第四步 判断x的类型并处理
1. 当返回是自己时
let promise = new Promise((resolve,rejected)=>{
resolve('ok')
}).then(data=>{
// 我等待我自己,但是我没有任何动作。这样做无意义,状态永远是pending态不会执行下面的方法
return promise
})
promise.then((data)=>{
console.log('ok')
},(err)=>{
console.log('err')
})
2.3.1 If
promiseandxrefer to the same object, rejectpromisewith aTypeErroras the reason.
解析:如果x等于promise2就抛出一个TypeError错误
// 核心 解析x 来决定promise2走向成功还是失败
function resolvePromise(promise2,x,resolve,reject){
// 判断x来决定promise2的关系 来判断有可能x是别人的promise 会有可能出问题
if(x==promise2){
return reject(new TypeError('出错了'))
}
}
2.当值不是promise时,我们就是按照之前的返回resolve(x)
2.3.3 Otherwise, if
xis an object or function,
2.3.4Ifxis not an object or function, fulfillpromisewithx
解析:如果x不是对象或者函数,直接让x作为promise的成功值
// 核心 解析x 来决定promise2走向成功还是失败
function resolveePromise(promise2,x,resolve,reject){
// 判断x来决定promise2的关系 来判断有可能x是别人的promise 会有可能出问题
if(x==promise2){
return reject(new TypeError('出错了'))
}
// 只有对象或者函数是promise
if ((typeof x === 'object' && x !== null) || typeof x === 'function') {
} else {
// 如果不是,肯定是个普通值(非promise的值)
resolve(x)
}
}
3.当值为promise时
再详细读下2.3.3里面的内容。
解析:取X上的then方法,并且x为函数的话,就用x作为then的this去执行。而且第一个参数为成功的promiseresolvePromise叫做y,第二个参数为失败的promiserejectPromise叫做r,并且当y还为resolvePromise时继续调用Resolve方法,直到它为一个普通值为止。
// 核心 解析x 来决定promise2走向成功还是失败
function resolvePromise(promise2, x, resolve, reject) {
// 判断x来决定promise2的关系 来判断有可能x是别人的promise 会有可能出问题
if (x == promise2) {
return reject(new TypeError('出错了'))
}
if ((typeof x === 'object' && x !== null) || typeof x === 'function') {
let called = false; // 表示没调用过成功和失败 保证别人的pormise不能即走成功又走失败
try {
let then = x.then; // 取x上的then方法
if (typeof then === 'function') {
then.call(x, y => { //x.then 如果这样写 有可能会再去取值
// y 可能还是一个promise 递归解析 y 直到它是一个普通值为止
if (called) return;
called = true;
resolvePromise(promise2, y, resolve, reject);
}, r => {
if (called) return;
called = true;
reject(r);
})
} else {
resolve(x)
}
} catch (e) {
// 如果取then方法失败,说明这不是一个promise 直接走失败的方法。
if (called) return;
called = true;
reject(e);
}
} else {
// 如果不是,肯定是个普通值
resolve(x);
}
}
完成
Promise有个规范测试,叫做 promises-aplus-tests 它是个全局命令,可以测试我们写的功能, 大家可以自己动手写完之后测试一下。
这样我们的初步目标达成了!完整代码:
const enum STATUS {
pending = 'PENDING',
fulfilled = 'FULFILLED',
rejected = 'REJECTED'
}
// 核心 解析x 来决定promise2走向成功还是失败
function resolvePromise(promise2: Promise, x: any, resolve: any, reject: any) {
// 判断x来决定promise2的关系 来判断有可能x是别人的promise 会有可能出问题
if (x == promise2) {
return reject(new TypeError('出错了'))
}
if ((typeof x === 'object' && x !== null) || typeof x === 'function') {
let called = false; // 表示没调用过成功和失败 保证别人的pormise不能即走成功又走失败
try {
let then = x.then; // 取x上的then方法
if (typeof then === 'function') {
then.call(x, (y: any) => { //x.then 如果这样写 有可能会再去取值
// y 可能还是一个promise 递归解析 y 直到它是一个普通值为止
if (called) return;
called = true;
resolvePromise(promise2, y, resolve, reject);
}, (r: any) => {
if (called) return;
called = true;
reject(r);
})
} else {
resolve(x)
}
} catch (e) {
// 如果取then方法失败,说明这不是一个promise 直接走失败的方法。
if (called) return;
called = true;
reject(e);
}
} else {
// 如果不是,肯定是个普通值
resolve(x);
}
}
class Promise {
static deferred: Function
status: STATUS
value: any
reason: any
onFulfilledCallbacks: Function[]
onRejectedCallbacks: Function[]
constructor(executor: (resolve: (value?: any) => void, reject: (reason?: any) => void) => void) {
this.status = STATUS.pending;
this.value = undefined;
this.reason = undefined;
this.onFulfilledCallbacks = [];
this.onRejectedCallbacks = []
const resolve = (value?: any) => {
if (this.status === STATUS.pending) {
this.status = STATUS.fulfilled;
this.value = value;
this.onFulfilledCallbacks.forEach(fn => fn());
}
}
const reject = (reason?: any) => {
if (this.status === STATUS.pending) {
this.status = STATUS.rejected;
this.reason = reason;
this.onRejectedCallbacks.forEach(fn => fn());
}
}
try {
executor(resolve, reject);
} catch (e) {
reject(e);
}
}
then(onFulfilled?: any, onRejected?: any) {
// 每次调用then都产生一个全新的Promise
onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : (val: any) => val;
onRejected = typeof onRejected === 'function' ? onRejected : (err: any) => { throw err };
let promise2 = new Promise((resolve, reject) => {
if (this.status == STATUS.fulfilled) {
setTimeout(() => {
try {
let x = onFulfilled(this.value);
// resolve(x);
// 也可以用
// promise2.resolve = resolve;
// promise2.reject=reject;
// resolvePromise(promise2,x);
resolvePromise(promise2, x, resolve, reject);
} catch (e) {
reject(e);
}
}, 0)
}
if (this.status == STATUS.rejected) {
setTimeout(() => {
try {
let x = onRejected(this.reason);
resolvePromise(promise2, x, resolve, reject);
} catch (e) {
reject(e)
}
}, 0)
}
if (this.status == STATUS.pending) {
this.onFulfilledCallbacks.push(() => {
// 切片编程 可以做额外的逻辑
setTimeout(() => {
try {
let x = onFulfilled(this.value);
resolvePromise(promise2, x, resolve, reject);
} catch (e) {
reject(e);
}
}, 0)
});
this.onRejectedCallbacks.push(() => {
setTimeout(() => {
try {
let x = onRejected(this.reason);
resolvePromise(promise2, x, resolve, reject);
} catch (e) {
reject(e);
}
}, 0)
});
}
})
return promise2;
}
}
Promise.deferred = function () {
let dfd = {} as any;
dfd.promise = new Promise((resolve, reject) => {
dfd.resolve = resolve;
dfd.reject = reject;
})
return dfd;
}
export default Promise;