Let’s keep Promises!

Akash Verma
4 min readMar 14, 2018

--

A Promise object represents a value that may not be available yet, but will be resolved at some point in the future. It allows you to write asynchronous code in a more synchronous fashion.Promises, have been around quite a while and are defined by a spec called Promise A+ spec. ES6 has adopted this spec for its Promise implementation; but there are other Promise libraries out there such as Q, Bluebird, RSVP and others that adhere to this spec and offer other features on top of it.

Promise States

A promise must be in one of three states: pending, fulfilled, or rejected.

a. When pending, a promise: may transition to either the fulfilled or rejected.

b. When fulfilled, a promise: must not transition to any other state and must . have a value, which must not change.

c. When rejected, a promise: must not transition to any other state and must have a reason, which must not change.

Promise Declaration

let PromiseObj= new Promise( /* executor */ function(resolve, reject) { ... } );

The executor function is executed immediately by the Promise implementation, passing resolve and reject functions (the executor is called before the Promise constructor even returns the created object). The resolve and reject functions, when called, resolve or reject the promise, respectively. The executor normally initiates some asynchronous work, and then, once that completes, either calls the resolve function to resolve the promise or else rejects it if an error occurred. If an error is thrown in the executor function, the promise is rejected. The return value of the executor is ignored.

In the above code snippet PromiseObj represents a value that will be available in future. then and catch functions are present on prototype object of the promise object.

On successful execution of resolve method:

  1. Promise state is transitioned from pending to resolved.
  2. function given as first argument to then() method of the promise object is executed with the first argument as the value with which the promise is resolved.
  3. Incase the response with which the promise is resolved is an object with then method, promise won’t be resolved until then method of the response object is not executed . The promise will resolve with the return of then method.

If reject method is executed:

  1. Promise state is transitioned from pending to rejected.
  2. catch method of the promise prototype object is executed or the function provided as the second argument to the then method is executed.
var p1 = new Promise( (resolve, reject) => {
resolve('Success!');
// or
// reject ("Error!");
} );

p1.then( value => {
console.log(value); // Success!
}, reason => {
console.log(reason); // Error!
} );

Promise chaining and then method

The then method returns a Promise which allows for method chaining.If the function passed as handler to then returns a Promise, an equivalentPromise will be exposed to the subsequent then in the method chain.

After the invocation of the handler function, if the handler function:

  • returns a value, the promise returned by then gets resolved with the returned value as its value;
  • throws an error, the promise returned by then gets rejected with the thrown error as its value;
  • returns an already resolved promise, the promise returned by then gets resolved with that promise's value as its value;
  • returns an already rejected promise, the promise returned by then gets rejected with that promise's value as its value.
  • returns another pending promise object, the resolution/rejection of the promise returned by then will be subsequent to the resolution/rejection of the promise returned by the handler. Also, the value of the promise returned by then will be the same as the value of the promise returned by the handler.
function resolveLater(resolve, reject) {
setTimeout(function () {
resolve(10);
}, 1000);
}
function rejectLater(resolve, reject) {
setTimeout(function () {
reject(20);
}, 1000);
}

var p1 = Promise.resolve('foo');
var p2 = p1.then(function() {
// Return promise here, that will be resolved to 10 after 1 second
return new Promise(resolveLater);
});
p2.then(function(v) {
console.log('resolved', v); // "resolved", 10
}, function(e) {
// not called
console.log('rejected', e);
});

var p3 = p1.then(function() {
// Return promise here, that will be rejected with 20 after 1 second
return new Promise(rejectLater);
});
p3.then(function(v) {
// not called
console.log('resolved', v);
}, function(e) {
console.log('rejected', e); // "rejected", 20
});

Writing your own simple Promise implementation

What we learnt until now is promise needs a state, an executor function ( let’s call it a callback function). In addition to that let’s use a variable named deferred to store the onResolved function. If promise is not resolved at that time, deferred will store onResolved else it will be undefined.This ensures onResolved function is called only after promise is resolved.

function Promise(fn) {
var state = 'pending';
var value;
var deferred;

function resolve(newValue) {
value = newValue;
state = 'resolved';

if(deferred) {
handle(deferred);
}
}

function handle(onResolved) {
if(state === 'pending') {
deferred = onResolved;
return;
}

onResolved(value);
}

this.then = function(onResolved) {
handle(onResolved);
};

fn(resolve);
}

Here Promise is a constructor function. Any object created with this constructor function will have then as object method as shown in above code.

handle function takes the onResolved function from then method and depending on promise state onResolved will be invoked immediately with resolved value ( if state is resolved) or stored in deferred variable (if state is pending).

resolve function will check deferred variable and invoke handle function with onResolved function as argument ( if resolve function was invoked after then ) else onResolved will be executed once then is invoked after promise is resolved.

This is a very simple promise implementation and can be extended further to follow standard A+ specs.

Conclusion

Promises have become an integral part of several idioms in JavaScript, including the WHATWG Fetch standard used for most modern ajax requests, and writing async execution for server side in Node. They provide a more readable alternative to the callback pattern, and are the foundation for the upcoming Async/Await pattern.

--

--

Akash Verma
Akash Verma

Written by Akash Verma

JavaScript Enthusiast, Software Engineer @goevive. Follow me on twitter @Akash940

No responses yet