Skip to content

Instantly share code, notes, and snippets.

@heartAndRain
Created March 1, 2021 09:42
Show Gist options
  • Save heartAndRain/95f46d89faaff7a82b6587031076d01a to your computer and use it in GitHub Desktop.
Save heartAndRain/95f46d89faaff7a82b6587031076d01a to your computer and use it in GitHub Desktop.
Promise A+ TypeScript
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