Last active
August 29, 2015 14:22
-
-
Save oeb25/3791a252a0d7505f229c to your computer and use it in GitHub Desktop.
A promise implementation in 85 LOC exluding comments
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
/** | |
* Promise Implementation in 85 LOC written in ES6 | |
* | |
* This doesn't follow the official promise spec 100%, and isn't ment to. | |
* I only made this as a challenge, and encurage no one to use | |
* it for production! | |
* | |
* I wrote this without having looked at any others implementations, and may | |
* be slightly off! | |
* | |
* This implementation uses no `this`, and is based all around closures. | |
* | |
* I would very much like your feedback on it, anything goes! | |
*/ | |
// Constants for state | |
const PENDING = 'pending'; | |
const RESOLVED = 'resolved'; | |
const REJECTED = 'rejected'; | |
// This is the promise starting point and can be called with and without new, | |
// but calling it without is encuraged since .this is not used. | |
export default function Promize(cb) { | |
// We create a new "waiter", the waiter is the backbone for the promise! | |
var w = waiter(); | |
// We CAN call the callback right away, since we DON'T need to let the user | |
// add .then and .catch beforehand! | |
cb(w.resolve, w.reject); | |
// We then return the promise methods. | |
return { then: w.then, 'catch': w.catch }; | |
}; | |
// This is the interesting stuff! | |
const waiter = () => { | |
// toCall is an array of the functions we would like to call as soon as the | |
// waiter completes it's previos operation. | |
var toCall = []; | |
// catchers is the same for "catchers". All the functions in here are called | |
// when any error occurs in the following .then's. | |
const catchers = []; | |
// Sets the current status to pending | |
var status = PENDING; | |
// The value of the promise when it has been resolved | |
var value; | |
// This is the basic `.then`. The callback can either return be a promise or | |
// a value. | |
const then = (cb) => { | |
// We create a waiter just as we did in the constructor. | |
const w = waiter(); | |
// We then add the callback to the list of functions to call as soon as | |
// this operation completes! | |
toCall.push({ | |
method: cb, | |
resolve: w.resolve | |
}); | |
// We create a switch statement to handle the function depending on state | |
switch (status) { | |
case RESOLVED: { | |
// If the promise has already been resolved, we recall the resolve. | |
resolve(); | |
break; | |
} | |
case REJECTED: { | |
// Same goes for the rejected. | |
rejected(); | |
break; | |
} | |
}; | |
// We again return the promise interface. | |
return { then: w.then, 'catch': w.catch }; | |
}; | |
// This is where we are told when a promise completes. | |
const resolve = () => { | |
// We set the sate of the promise to be resolved | |
status = RESOLVED; | |
if (typeof value === 'undefined') | |
value = arguments[0]; | |
// We go through every callback added to the toCall. | |
toCall.forEach(a => { | |
try { | |
// We run the method added, and store it's result in result. | |
const result = a.method(value); | |
// We check if the result is a promise it self. | |
if (result && result.then) | |
// If it is, we return a promise, with the resolve of the next | |
// promise or function. | |
return result.then(a.resolve); | |
// If it's not a promise, we simple run the resolve with the result. | |
a.resolve(result); | |
// If any errors occur they will be caught here. | |
} catch (e) { | |
// If no "catchers" is added, we simply throw the error. If there is a | |
// catch at an erlier stage in the promise, it will make it's way back | |
// there! | |
if (catchers.length < 1) | |
throw e; | |
// We set the state to rejected | |
state = REJECTED; | |
// We do the same as with the results. In this way we can have multiple | |
// catchers to the same promise chain! | |
catchers.forEach(c => c(e)); | |
} | |
}); | |
// We clear the toCall array to prevent running the same `.then` twice. | |
toCall = []; | |
}; | |
// reject simply throws an error with the supplyied message! | |
const reject = msg => { | |
// We set the state to rejected | |
state = REJECTED; | |
throw new Error(msg); | |
}; | |
// _chatch addes the "catcher" to the list of "catchers", in the same way | |
// .then does! It then returns the same .then and catch instead of | |
// creating a new. | |
// It's named "_catch" because "catch" is a reserved word. | |
const _catch = (e) => { | |
onError.push(e); | |
// We again return the promise interface! | |
return { then, 'catch': _catch }; | |
}; | |
// Returns the functions. | |
return { then, resolve, reject, 'catch': _catch }; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment