What is a promise in JavaScript?

JavaScript is single threaded, meaning that two bits of script cannot run at the same time; they have to run one after another. A Promise is an object that represents the eventual completion (or failure) of an asynchronous operation, and its resulting value.

var promise = new Promise(function(resolve, reject) {
  // do thing, then…

  if (/* everything worked */) {
    resolve("See, it worked!");
  }
  else {
    reject(Error("It broke"));
  }
});

A Promise exists in one of these states

  • Pending: initial state, neither fulfilled nor rejected.
  • Fulfilled: operation completed successfully.
  • Rejected: operation failed.

The Promise object works as proxy for a value not necessarily known when the promise is created. It allows you to associate handlers with an asynchronous action’s eventual success value or failure reason.

This lets asynchronous methods return values like synchronous methods: instead of immediately returning the final value, the asynchronous method returns a promise to supply the value at some point in the future.

Using ‘Then’ (Promise Chaining)

To take several asynchronous calls and synchronize them one after the other, you can use promise chaining. This allows using a value from the first promise in later subsequent callbacks.

Promise.resolve('some')
  .then(function(string) { // <-- This will happen after the above Promise resolves (returning the value 'some')
    return new Promise(function(resolve, reject) {
      setTimeout(function() {
        string += 'thing';
        resolve(string);
      }, 1);
    });
  })
  .then(function(string) { // <-- This will happen after the above .then's new Promise resolves
    console.log(string); // <-- Logs 'something' to the console
  });

Promise API

There are 4 static methods in the Promise class:

  • Promise.resolve
  • Promise.reject
  • Promise.all
  • Promise.race

Promises can be chained together

When writing Promises to solve a particular problem, you can chain them together to form logic.

var add = function(x, y) {
  return new Promise((resolve,reject) => {
    var sum = x + y;
    if (sum) {
      resolve(sum);
    }
    else {
      reject(Error("Could not add the two values!"));
    }
  });
};

var subtract = function(x, y) {
  return new Promise((resolve, reject) => {
    var sum = x - y;
    if (sum) {
      resolve(sum);
    }
    else {
      reject(Error("Could not subtract the two values!"));
    }
  });
};

// Starting promise chain
add(2,2)
  .then((added) => {
    // added = 4
    return subtract(added, 3);
  })
  .then((subtracted) => {
    // subtracted = 1
    return add(subtracted, 5);
  })
  .then((added) => {
    // added = 6
    return added * 2;    
  })
  .then((result) => {
    // result = 12
    console.log("My result is ", result);
  })
  .catch((err) => {
    // If any part of the chain is rejected, print the error message.
    console.log(err);
  });

This is useful for following a Functional Programming paradigm. By creating functions for manipulating data you can chain them together to assemble a final result. If at any point in the chain of functions a value is rejected the chain will skip to the nearest catch() handler.

For more information on Functional Programming: Functional Programming

Function Generators

In recent releases, JavaScript has introduced more ways to natively handle Promises. One such way is the function generator. Function generators are “pausable” functions. When used with Promises, generators can make using a lot easier to read and appear “synchronous”.

const myFirstGenerator = function* () {
  const one = yield 1;
  const two = yield 2;
  const three = yield 3;

  return 'Finished!';
}

const gen = myFirstGenerator();

Here’s our first generator, which you can see by the function* syntax. The gen variable we declared will not run myFirstGenerator, but instead will “this generator is ready to use”.

console.log(gen.next());
// Returns { value: 1, done: false }

When we run gen.next() it will unpause the generator and carry on. Since this is the first time we have called gen.next() it will run yield 1 and pause until we call gen.next() again. When yield 1 is called, it will return to us the value that was yielded and whether or not the generator is done.

console.log(gen.next());
// Returns { value: 2, done: false }

console.log(gen.next());
// Returns { value: 3, done: false }

console.log(gen.next());
// Returns { value: 'Finished!', done: true }

console.log(gen.next());
// Will throw an error

As we keep calling gen.next() it will keep going onto the next yield and pausing each time. Once there are no more yield’s left, it will proceed to run the rest of the generator, which in this case simply returns 'Finished!'. If you call gen.next() again, it will throw an error as the generator is finished.

Now, imagine if each yield in this example was a Promise, the code itself would appear extremely synchronous.

Promise.all(iterable) is very usefull for multiple request to different source

The Promise.all(iterable) method returns a single Promise that resolves when all of the promises in the iterable argument have resolved or when the iterable argument contains no promises. It rejects with the reason of the first promise that rejects.

var promise1 = Promise.resolve(catSource);
var promise2 = Promise.resolve(dogSource);
var promise3 = Promise.resolve(cowSource);

Promise.all([promise1, promise2, promise3]).then(function(values) {
  console.log(values);
});
// expected output: Array ["catData", "dogData", "cowData"]

More info on Promises: