Javascript Promises

javascript

https://www.sitepoint.com/overview-javascript-promises/

// JavaScript - Promises:

While synchronous code is easier to follow and debug, async is generally better 
for performance and flexibility. Why "hold up the show" when you can trigger 
numerous requests at once and then handle them when each is ready?  Promises are 
becoming a big part of the JavaScript world, with many new APIs being 
implemented with the promise philosophy.

A Promise object represents a value that may not be available yet, but will be 
available at some point in the future. It allows us to write asynchronous code 
in a more synchronous fashion. For example, if we use the promise API to make 
an asynchronous call to a remote web service we will create a Promise object 
which represents the data that will be returned by the web service in future. 
It will become available when the request completes and a response comes back 
from the web service. We can attach callbacks to the Promise object which will 
be called once the actual data is available.

function fetchRandomQuote() {
  var promise = new Promise(function(resolve, reject) {
    var request = new XMLHttpRequest();
    request.open('GET', 'http://api.icndb.com/jokes/random');
    request.onload = function() {
      if (request.status == 200) {
        resolve(request.response); // we got data here, so resolve the Promise
      } else {
        reject(Error(request.statusText)); // status is not 200 OK, so reject
      }
    };

    request.onerror = function() {
      reject(Error('Error fetching data.')); // error occurred, reject the  Promise
    };

    request.send(); //send the request
  });
  return promise;
}

var promise = fetchRandomQuote();
promise
  .then(function(data) {
    console.log('Quote: ' + data);
  })
  .catch(function(error) {
    console.log(error.getMessage());
  });

We start by instantiating a new Promise object and passing it a callback 
function. The callback takes two arguments, resolve and reject, which are 
both functions. All your asynchronous code goes inside that callback. If 
everything is successful, the promise is fulfilled by calling resolve(). In 
case of an error, reject() is called with an Error object.

In the above code, when a JSON response is received from the remote server, it 
is passed to the resolve() method. In case of any error, reject() is called with 
an Error object.

The .then method can take two parameters, which are both functions / callbacks.  
The first parameter is the success callback, and the second parameter is the 
failure callback.  However, this can make our code harder to read.  Therefore, 
we should only specify one parameter, the success callback, and we should use 
the .catch method to specify the failure callback.  We can specify multiple 
success callbacks, and multiple failure callbacks.

When the promise is resolved, the success callbacks are invoked.  When the 
promise rejected, the failure callbacks are invoked.

If we specify multiple success callbacks, only the first callback receive the 
data that was passed to the resolve method. If the first success callback return 
a value or object, that value or object will be passed to the second success 
callback as a parameter.

A promise can have 3 states:

1. pending (not resolved or rejected)
2. fulfilled
3. rejected

The promise.status property, which is code-inaccessible and private, contains 
information about these states.  When a promise is rejected or resolved, this 
status is permanently associated with it.  This means that a promise can be 
success or failed only once.  If a promise has already been fulfilled (either 
resolved or rejected), and we later attach callbacks to it, the callbacks will 
be immediately invoked.

It is sometimes desirable to chain promises together.  For example, we might 
have multiple asynchronous operations that we need to perform.  When one 
operation give us data, we will start to do some other operation on that piece 
of data, and so on.  In such cases, we can chain together promises:

function getPromise(url) {
  // return a promise here.  Send an async request to the url, and when 
  // we receive the data, resolve the promise
}

var promise = getPromise('some url');
promise
  .then(function(result) {
    return getPromise(result); // return another promise
  })
  .then(function(result) {
    // handle the final result
  });

In the above code, when the first promised is resolved, both "then" callbacks 
are invoked in the order that we specified.  The second "then" callback is not 
invoked until the first "then" callback is finished.  The return value of the 
first then callback, which is another promise, is passed to the second "then" 
callback as a parameter.

When we return a simple value inside then(), the next then() is called with that 
return value.  But if we return a promise inside then(), the next then() waits 
on it and gets called when that promise is settled.  Is this really the case?  
Does the second then callback really receive the data from the second promise or 
does it receive the second promise as a parameter?

If the promise is rejected, the control will move forward to the next then() 
that has a failure callback, or to the next catch().  Apart from explict promise 
rejection, the catch callback is also called when any exception is thrown.

Sometimes, we do not need to complete an async task within the promise.  In that 
case, we can simply invoke Promise.resolve() or Promise.reject() without using 
the new keyword:

var userCache = {};

function getUserDetail(username) {
  // In both cases, cached or not, a promise will be returned

  if (userCache[username]) {
    // Return a promise without the "new" keyword
    return Promise.resolve(userCache[username]);
  }

  // Use the fetch API to get the information
  // fetch returns a promise
  return fetch('users/' + username + '.json')
    .then(function(result) {
      userCache[username] = result;
      return result;
    })
    .catch(function() {
      throw new Error('Could not find user: ' + username);
    });
}

The above code use Promise.resolve() and the fetch function, and 
in both of these cases, it always return a promise, not the user data.  
The user's data is passed to the subsequent then callback.

In the above code, only minimal stuff is done inside the function, and 
because it always return a promise, the caller of our function can do 
additional processing, or error handling by adding additional then 
callbacks or catch callbacks.

Each then callback receive the result of the previous then callback return value.

If a promise has already been resolved but then is called again, the then 
callback is immediately invoked.  If the promise was rejected, and we call the 
then function, the then callback is never invoked.

There are times when we want to trigger multiple independent asynchronous calls 
but only want to respond when all of them are completed.  That is when we need 
to use the Promise.all method, which takes an array of promises, and invoke 
its callback when all of these promises are resolved:

Promise.all([promise1, promise2])
  .then(function(results) {
    // Both promises were resolved.
  })
  .catch(function(error) {
    // One or more promises was rejected
  });
);

In the above code, results (the parameter of the then callback) is an array 
containing the results of the last then callback of each of the promises that were 
passed to Promise.all.

The Promise.all method returns a promise that either fulfills when all of the 
promises in the iterable argument have fulfilled or rejects as soon as one of 
the promises in the iterable argument rejects. If the returned promise fulfills, 
it is fulfilled with an array of the values from the fulfilled promises in same 
order as defined in the iterable. If the returned promise rejects, it is 
rejected with the reason from the first promise in the iterable that rejected. 
This method can be useful for aggregating results of multiple promises.

Promise.reject(reason): Returns a Promise object that is rejected with the given 
reason.

Promise.resolve(value): Returns a Promise object that is resolved with the 
given value. If the value is a thenable (i.e. has a then method), the returned 
promise will "follow" that thenable, adopting its eventual state; otherwise the 
returned promise will be fulfilled with the value. Generally, if you don't know 
if a value is a promise or not, Promise.resolve(value) it instead and work with 
the return value as a promise.
var p = new Promise((resolve, reject) => {
  setTimeout(() => {
    if (true) {
      resolve('success data');
    } else {
      reject('An error has occurred.');
    }
  }, 2000);
});

p.then((data) => {
  console.log(data);
});

p.catch((error) => {
  console.error(error);
});

The callback that is specified as the first parameter for the .then method is 
invoked when the promise is resolved.  The callback function specified as the 
first parameter of the .catch method is invoked when the promise is rejected.

We can use the .then method multiple times, and the callbacks are called in the 
order specified.  Each callback receive the result of the previous callback.  
In other words, callbackA is specified before callbackB, callbackB will not be 
invoked until callbackA has finished.

If we do not wish to use the .catch method, we can specify a "failure" callback 
as the second parameter of the then method.

p.then((data) => console.log('success: ', data), (error) => console.error(error));

The above code is a bit hard to read, so we should write it as:

p.then(
  (data) => {
    console.log('success: ', data);
  },
  (error) => {
    console.error(error);
  }
);

As a best practice, we should always use the the .catch method instead of 
specifying the "failure" callback as the second parameter to the .then method.

When we resolve a promise, we can specify the data, which is passed to the 
callback of the .then method.

If we do use multiple .then statement, only the first callback receives the data 
that was passed to the resolve method.  Sub-sequent .then callbacks do not 
receive this data.  Instead, they receive the result of the previous .then 
callbacks.

If an error is thrown, it automatically invoke the callback methods that are 
specified using the the .catch method.

Sub-sequent "then" callbacks are not invoked if an error occurred within the 
first "then" callback.
The process of setting a value to the promise is called "resolving the promise".  
If we can not assign a value to a promise (failure condition), we reject the 
promise with a reason.  Resolving or rejecting a promise is called fulfilling 
the promise.

var promise = asyncFunction();

promise.then(function(value) {
  // do something with the value
});

return promise;

Saving the promise inside a variable is not always required, and we can often 
omit it:

return asyncFunction().then(function(value) {
  // do something with the value here
});

The .then method always returns a promise, which may be the same promise that it 
was invoked with (the this object), or it may be a new promise.  This allows us 
to chain multiple then calls:

return asyncFunction()
  .then(function(value) {...})  // first callback
  .then(function(value) {...}); // second callback
Unless otherwise stated, the content of this page is licensed under Creative Commons Attribution-ShareAlike 3.0 License