Last active
September 3, 2020 07:35
-
-
Save shalvah/0c21ff0102aabb683d00ddaa91f63e82 to your computer and use it in GitHub Desktop.
Simplified implementation of a promise, inspired by https://exploringjs.com/deep-js/ch_implementing-promises.html
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
/** | |
* Simple promise implementation. Doesn't have all the features of the actual Promise API, + assumes you know what you're doing. | |
*/ | |
class ToyPromise { | |
constructor(cb) { | |
this.state = 'pending'; | |
this._promiseResult = undefined; | |
this._fulfillmentCallbacks = []; | |
this._rejectionCallbacks = []; | |
// cb is the (resolve, reject) => {} callback the user passes when creating a Promise. | |
// The resolve and reject parameters are actually our internal _resolve and _reject methods, ha! | |
cb(this._resolve.bind(this), this._reject.bind(this)); | |
} | |
// Helper method to queue a list of functions for execution asynchronously | |
_queueTasks(...tasks) { | |
tasks.forEach(t => setTimeout(t, 0)); | |
} | |
then(onFulfilled) { | |
// First rule: .then() returns a new Promise | |
return new ToyPromise((res, rej) => { | |
const fulfillmentTask = () => { | |
try { | |
const result = onFulfilled(this._promiseResult); | |
// The new Promise should resolve when the then() callback is done | |
res(result); | |
} catch (e) { | |
// The new Promise should reject if an error was thrown in the then() callback | |
rej(e); | |
} | |
}; | |
// Back to the original Promise... | |
if (this.state === 'fulfilled') { | |
// If the Promise is already done when .then(cb) is called, execute cb immediately (asynchronously!) | |
this._queueTasks(fulfillmentTask); | |
} else if (this.state === 'pending') { | |
// Otherwise, store the callback for later. | |
// Note that we're pushing onto an array rather than overwriting a value, | |
// so that you can call .then() multiple times on the same Promise if you like (i.e. no chaining) | |
this._fulfillmentCallbacks.push(fulfillmentTask); | |
} | |
}); | |
} | |
catch(onRejected) { | |
return new ToyPromise((res, rej) => { | |
const rejectionTask = () => { | |
try { | |
const result = onRejected(this._promiseResult); | |
res(result); | |
} catch (e) { | |
rej(e); | |
} | |
} | |
if (this.state === 'rejected') { | |
this._queueTasks(rejectionTask); | |
} else if (this.state === 'pending') { | |
this._rejectionCallbacks.push(rejectionTask); | |
} | |
}); | |
} | |
// The function that actually resolves the Promise. | |
// When you write `new Promise((resolve, reject)) => resolve(value))`, THIS is the resolve() you're calling | |
_resolve(value) { | |
if (this.state !== 'pending') return this; | |
this.state = 'fulfilled'; | |
this._promiseResult = value; | |
// Execute all the registered callbacks and empty the list | |
this._queueTasks(...this._fulfillmentCallbacks); | |
this._fulfillmentCallbacks = []; | |
this._rejectionCallbacks = []; | |
} | |
_reject(value) { | |
if (this.state !== 'pending') return this; | |
this.state = 'rejected'; | |
this._promiseResult = value; | |
this._queueTasks(...this._rejectionCallbacks); | |
this._fulfillmentCallbacks = []; | |
this._rejectionCallbacks = []; | |
} | |
} | |
// Usage | |
const prom = new ToyPromise((res, rej) => { | |
setTimeout(() => res(6), 2000); | |
}); | |
prom.then(v => { | |
console.log("Expected 6; resolved with " + v); | |
return 10; | |
}); // Note that the "10" here is discarded, since we didn't chain on the new Promise returned | |
prom.then(v => { | |
console.log("Expected 6; resolved with " + v); | |
return 11; | |
}).then(v => { | |
console.log("Expected 11; resolved with " + v); | |
throw new Error('catch me if you can'); | |
}).catch(e => { | |
console.log("Expected 'Error: catch me if you can'; rejected with " + e); | |
return "Final value"; | |
}).then(console.log); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment