Created
March 1, 2021 09:42
-
-
Save heartAndRain/95f46d89faaff7a82b6587031076d01a to your computer and use it in GitHub Desktop.
Promise A+ TypeScript
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
enum State { | |
'pending' = 'pending', | |
'fulfilled' = 'fulfilled', | |
'rejected' = 'rejected' | |
} | |
type CallbackPayload = { | |
onFulfilled: (val: any) => any, | |
onRejected: (reason: any) => any, | |
resolve: PromiseAPlus['resolve'], | |
reject: PromiseAPlus['reject'] | |
} | |
class PromiseAPlus { | |
private state: State = State.pending | |
private callbackQueue: Array<CallbackPayload> = []; | |
private val: any | |
private err: any | |
constructor(creator?: (resolve: PromiseAPlus['resolve'], reject?: PromiseAPlus['reject']) => any) { | |
if (creator) { | |
try { | |
creator(this.resolve.bind(this), this.reject.bind(this)); | |
} catch (error) { | |
this.reject(error); | |
} | |
} | |
} | |
public static resolve(val: any) { | |
if (val instanceof PromiseAPlus) { | |
return val; | |
} | |
return new PromiseAPlus(resolve => resolve(val)); | |
} | |
public static reject(err: any) { | |
return new PromiseAPlus((_, reject) => reject(err)); | |
} | |
public static all(arr: PromiseAPlus[]) { | |
let cnt = 0; | |
const res = Array(arr.length); | |
return new PromiseAPlus((resolve, reject) => { | |
for (let i = 0; i < arr.length; i++) { | |
PromiseAPlus.resolve(arr[i]) | |
.then(v => { | |
res[i] = v; | |
cnt++; | |
if (cnt === arr.length) { | |
resolve(res); | |
} | |
}, e => reject(e)); | |
} | |
}); | |
} | |
public static allSettled(arr: PromiseAPlus[]) { | |
return PromiseAPlus.all(arr.map(item => | |
item | |
.then(v => ({ status: 'fulfilled', value: v }), e => ({ status: 'rejected', reason: e })) | |
)); | |
} | |
public static race(arr: PromiseAPlus[]) { | |
return new PromiseAPlus((resolve, reject) => { | |
let hasResolved = false; | |
for (const item of arr) { | |
if (hasResolved) break; | |
PromiseAPlus.resolve(item) | |
.then(v => { | |
resolve(v); | |
hasResolved = true; | |
}, e => reject(e)); | |
} | |
}); | |
} | |
private resolve(val?: any) { | |
if (this.state !== State.pending) return; | |
this.state = State.fulfilled; | |
this.val = val; | |
this.execCallbackQueue(); | |
} | |
private reject(error?: any) { | |
if (this.state !== State.pending) return; | |
this.state = State.rejected; | |
this.err = error; | |
this.execCallbackQueue(); | |
} | |
private execCallback(payload: CallbackPayload) { | |
const { onFulfilled, onRejected, resolve, reject } = payload; | |
if (this.state === State.fulfilled) { | |
if (typeof onFulfilled !== 'function') { | |
resolve(); | |
return; | |
} | |
try { | |
const res = onFulfilled(this.val); | |
if (res instanceof PromiseAPlus) { | |
res.then(val => resolve(val), err => reject(err)); | |
} else { | |
resolve(res); | |
} | |
} catch (error) { | |
reject(error); | |
} | |
} else if (this.state === State.rejected) { | |
if (typeof onRejected !== 'function') { | |
reject(this.err); | |
return; | |
} | |
try { | |
const res = onRejected(this.err); | |
if (res instanceof PromiseAPlus) { | |
res.then(val => resolve(val), err => reject(err)); | |
} else { | |
resolve(res); | |
} | |
} catch (error) { | |
reject(error); | |
} | |
} | |
} | |
private execCallbackQueue() { | |
for (const item of this.callbackQueue) { | |
this.execCallback(item); | |
} | |
} | |
public then(onFulfilled?: (val: any) => any, onRejected?: (err: any) => any) { | |
return new PromiseAPlus((resolve, reject) => { | |
const payload = { | |
onFulfilled, | |
onRejected, | |
resolve, | |
reject | |
}; | |
if (this.state === State.pending) { | |
this.callbackQueue.push(payload); | |
} else { | |
this.execCallback(payload); | |
} | |
}) | |
} | |
public catch(onRejected?: (err: any) => any) { | |
return this.then(undefined, onRejected); | |
} | |
public finally(callback?: () => any) { | |
return this.then( | |
v => { | |
callback && callback(); | |
return PromiseAPlus.resolve(v); | |
}, | |
e => { | |
callback && callback(); | |
return PromiseAPlus.reject(e); | |
} | |
) | |
} | |
} | |
// const p = new PromiseAPlus((resolve) => { | |
// console.log('promise exec'); | |
// throw new Error('aa'); | |
// // setTimeout(() => { | |
// // resolve('hahhahaha'); | |
// // }, 1000); | |
// }); | |
// p.then((val) => { | |
// console.log('then1', val); | |
// return 'wowoowo' | |
// }) | |
// .then((val) => { | |
// console.log('then2', val); | |
// }) | |
// setTimeout(() => { | |
// p.then((val) => { | |
// console.log('then3', val); | |
// }) | |
// }, 2000) | |
// console.log(p); | |
// p | |
// .then((val) => { | |
// console.log('then3', val); | |
// }) | |
// .then(() => { | |
// console.log('sus'); | |
// }) | |
// .catch((e) => { | |
// console.log('errr', e); | |
// return 3; | |
// }) | |
// .finally(() => { | |
// console.log('ttttt'); | |
// }) | |
// .then(v => { | |
// console.log('thenthen', v); | |
// }) | |
// PromiseAPlus.all([ | |
// PromiseAPlus.resolve(3), | |
// PromiseAPlus.reject(4) | |
// ]).then(v => { | |
// console.log(v); | |
// }, e => { | |
// console.error('err', e); | |
// }) | |
// PromiseAPlus.race([ | |
// new PromiseAPlus((resolve) => setTimeout(() => resolve(1), 2000)), | |
// new PromiseAPlus((resolve) => setTimeout(() => resolve(2), 1000)), | |
// ]).then(v => { | |
// console.log(v); | |
// }) | |
PromiseAPlus.allSettled([ | |
new PromiseAPlus((resolve, reject) => setTimeout(() => reject(1), 2000)), | |
new PromiseAPlus((resolve) => setTimeout(() => resolve(2), 1000)), | |
]).then(v => { | |
console.log(v); | |
}) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment