Write a function in JavaScript that implements polyfill of the promise.
Promises in JavaScript allow you to execute non-blocking (asynchronous) code and produces a value if the operation is successful or throws an error when the process fails.
In short, the eventual success (or failure) of an asynchronous operation and its associated value are represented by the Promise object.
Anotomy of promise
const promise = new Promise((resolve, reject) => { // time-consuming async operation // initial state will be pending // any one of the below operations can occur at any given time // this will resolve or fulfill the promise resolve(value); // this will reject the promise reject(reason); }); // this will be invoked when a promise is resolved promise.then((value) => { }); // this will be invoked when a promise is rejected promise.catch((value) => { }); // this will always be invoked after any of the above operation promise.then((value) => { });
Example
const promise = new Promise((resolve, reject) => { setTimeout(() => { resolve("hello"); }, 1000); }); promise.then((value) => { console.log(value); });
We have to implement a custom function MyPromise
that will be similar to the original promise.
To implement this we will use the observer pattern.
Use two handlers onSuccess
and onError
and assign this whenever .then
, .catch
, .finally
methods are called.
Whenever the resolve
or reject
methods are invoked, run all the handlers in sequence and pass down the values to the next.
// enum of states const states = { PENDING: 0, FULFILLED: 1, REJECTED: 2 } class MyPromise { // initialize the promise constructor(callback) { this.state = states.PENDING; this.value = undefined; this.handlers = []; try { callback(this._resolve, this._reject); } catch (error) { this._reject(error); } } // helper function for resolve _resolve = (value) => { this._handleUpdate(states.FULFILLED, value); } // helper function for reject _reject = (value) => { this._handleUpdate(states.REJECTED, value); } // handle the state change _handleUpdate = (state, value) => { if (state === states.PENDING) { return; } setTimeout(() => { if (value instanceof MyPromise) { value.then(this._resolve, this._reject); } this.state = state; this.value = value; this._executeHandlers(); }, 0) } // excute all the handlers // depending on the current state _executeHandlers = () => { if (this.state === states.PENDING) { return; } this.handlers.forEach((handler) => { if (this.state === states.FULFILLED) { return handler.onSuccess(this.value); } return handler.onFailure(this.value); }) this.handlers = []; } // add handlers // execute all if any new handler is added _addHandler = (handler) => { this.handlers.push(handler); this._executeHandlers(); } // then handler // creates a new promise // assisgnes the handler then = (onSuccess, onFailure) => { // invoke the constructore // and new handler return new MyPromise((resolve, reject) => { this._addHandler({ onSuccess: (value) => { if (!onSuccess) { return resolve(value); } try { return resolve(onSuccess(value)); } catch (error) { reject(error); } }, onFailure: (value) => { if (!onFailure) { return reject(value); } try { return reject(onFailure(value)); } catch (error) { return reject(error); } } }) }) }; // add catch handler catch = (onFailure) => { return this.then(null, onFailure); }; // add the finally handler finally = (callback) => { // create a new constructor // listen the then and catch method // finally perform the action return new MyPromise((resolve, reject) => { let wasResolved; let value; this.then((val) => { value = val; wasResolved = true; return callback(); }).catch((err) => { value = err; wasResolved = false; return callback(); }) if (wasResolved) { resolve(value); } else { reject(value); } }) }; };
Input: const promise = new MyPromise((resolve, reject) => { setTimeout(() => { resolve("hello"); }, 1000); }); promise.then((value) => { console.log(value); }); Output: "hello"
Credit- original implementation.