Skip to content

Instantly share code, notes, and snippets.

@Yengas
Last active May 19, 2018 18:56
Show Gist options
  • Select an option

  • Save Yengas/64d9f35f95dcb7992ada338a4cbefc8a to your computer and use it in GitHub Desktop.

Select an option

Save Yengas/64d9f35f95dcb7992ada338a4cbefc8a to your computer and use it in GitHub Desktop.
A promise implementation without any spec compliance. Just for fun.
class MyPromise{
// create a new instance of a promise
// callback is a function that accepts resolve and reject functions as params
constructor(callback){
this.resolved = false;
this.reject = false;
this.value = null;
// to temporarily hold promises that got created while this promise was in pending state
this.waiting = [];
try{
callback(
// the resolve function calls on finish with the first param true
(value) => this.onFinish(true, value),
// catch with the second param as false
(value) => this.onFinish(false, value),
);
}catch(e){
// reject in case the callback throws an error
this.onFinish(false, e);
}
}
// resolve/reject this promise
onFinish(resolved, value){
// we don't want to resolve already resolved/rejected promises.
if(this.resolved || this.reject) return null;
// set the state of the promise
if(resolved) this.resolved = true;
else this.reject = true;
// hold the resolved value
this.value = value;
// which function to call in the waiting list
const idx = this.resolved ? 0 : 1;
// either call all resolves or rejects from the waiting arr.
this.waiting.forEach((arr) => arr[idx](value));
// we don't need to hold the waiting values for this promise anymore, since it resolved.
this.waiting = null;
}
// call/store resolve/reject methods of newly created promises
process(onResolve, onReject){
// if this promise is resolved or rejected, just call the proper method with the stored value
if(this.resolved || this.reject)
return this.resolved ? onResolve(this.value) : onReject(this.value);
// otherwise, we need to wait until the promise resolves.
this.waiting.push([onResolve, onReject]);
}
// given a value, and a mapper function, call resolve/reject methods safely.
wrapCallSafe(value, mapper, resolve, reject){
try{
const result = mapper(value);
// if the result is promise, wait for it resolve (unwrapping)
if(result && result.constructor == MyPromise){
result.then(resolve).catch(reject);
}else{
// else just resolve with the result
resolve(result);
}
}catch(e){
// if any errors occur in the mapper func, call reject
reject(e);
}
};
createNewPromise(isThen, mapper){
return new MyPromise((resolve, reject) => {
// create a onResolve/onReject functions that safely calls resolve/reject of our promise library, by mapping the value
const onResolve = (value) => {
// if the promise is created with .then call, we need to map the value before calling resolve
this.wrapCallSafe(value, isThen ? mapper : id, resolve, reject);
}
const onReject = (value) => {
// if the promise is created with .catch call, we need to map the value before calling reject/resolve
// in case onReject was called, on a Promise we created with .then, we need to reject.
this.wrapCallSafe(value, isThen ? id : mapper, isThen ? reject : resolve, reject);
}
// call or store our onResolve/onReject methods according to promise state
this.process(onResolve, onReject);
});
}
then(func, func2){
const newPromise = this.createNewPromise(true, func);
return func2 ? newPromise.catch(func2) : newPromise;
}
catch(func){
return this.createNewPromise(false, func);
}
}
function id(x){ return x; };
// resolved unit function for promise
function resolve(value){
if(value.constructor == MyPromise){
return new MyPromise((resolve) => value.then(resolve).catch(resolve));
}
return new MyPromise((resolve) => resolve(value));
}
// reject unit function for promise
function reject(err){
if(err.constructor == MyPromise){
return new MyPromise((_, reject) => err.then(reject).catch(reject));
}
return new MyPromise((_, reject) => reject(err));
}
async function main(){
const test1 = new MyPromise((resolve, reject) => setTimeout(resolve, 1000, 5))
.then((x) => { throw new Error('asd'); })
.catch((x) => 10)
.catch((y) => 15)
.then((x) => resolve(resolve(x + 10)));
console.log('wtf');
console.log(await test1); // await works correctly...
console.log('waiting for reject...');
await reject(new Error('yo!')); // this register on .then aswell
}
main().catch((e) => console.log('caught', e));
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment