Promises and async/await

A Promise is like a contract: it states that a value will be available in the future when a long-running operation has completed. In essence, a Promise represents the result of an asynchronous operation. When that value has been determined, the Promise executes the given code or handles any error associated with not having received the expected value.

Promises are first-class citizens of the JavaScript specification. They have three states:

  • PendingThe initial state of the Promise
  • FulfilledThe asynchronous operation successfully resolved
  • RejectedThe asynchronous operation did not successfully resolve

Promises

When a Promise has been resolved, successfully or not, its value can’t change; it becomes immutable. We’ll discuss immutability in the section on functional programming later in this appendix.

To set up a Promise, you create a function that accepts two callback functions: one that executes on success and one that executes on failure. These callbacks fire when called by the Promise execution. Then, execution of the callbacks is transferred to then() functions (which are chainable) on success or a catch() function when not.

Listing D.47. Setting up/using a Promise
const promise = new Promise((resolve, reject) => {             1
  // set up long running, possibly asynchronous operation,
  // like an API query
  if (/* successfully resolved */) {
    resolve({data response});                                  2
  } else {
    reject();                                                  3
  }
});

promise
  .then((data) => {/* execute this on success */})             4
  .then(() => {/ * chained next function, and so on */})       5
  .catch((err) => {/* handle error */});                       6

  • 1 Creates a Promise, passing in the expected callback function
  • 2 On success, calls the resolve() function, optionally passing data forward
  • 3 On failure, calls the reject() function, optionally passing data or the Error object forward
  • 4 The then() function is called; it performs the desired operation, optionally returning a value to next then().
  • 5 The next then() function in the chain, which can be as long as necessary
  • 6 Catches the error. If this is at the end of a chain of then() functions, any error thrown is caught by this handler.

We use Promises in the Loc8r application, but not in a complicated way. The Promises API provides some static functions that help if you’re trying to execute multiple Promises.

Promise.all() accepts an iterable of Promises and returns a Promise when all items in the array fulfill or reject. The resolve() callback receives an array of responses: a mixture of Promise-like objects and other objects in order of fulfillment. If one of the executed Promises rejects, the reject() callback receives a single value.

Listing D.48. Promise.all()
const promise1 = new Promise((resolve, reject) => resolve() );
const promise2 = new Promise((resolve, reject) => resolve() );
const promise3 = new Promise((resolve, reject) => reject() );
const promise4 = new Promise((resolve, reject) => resolved() );

Promise.all([
  promise1,
  promise2,
  promise3,
  promise4
])
.then(([]) => {/* process success data iterable */})      1
.catch(err => console.log(err));                          2

  • 1 Promise 3 rejects, so this is ignored (in this example).
  • 2 The reject() call on Promise 3 ends up here, although all Promises are executed.

Promise.race() also accepts an iterable, but the output of Promise.race() is different. Promise.race() executes all provided Promises and returns the first response value that it receives whether this value is a fulfillment or a rejection.

Listing D.49. Promise.race()
const promise1 = new Promise((resolve, reject) =>
setTimeout(resolve, 1000, 'first') );
const promise2 = new Promise((resolve, reject) =>
setTimeout(reject, 200, 'second') );

Promise.race([promise1, promise2])
  .then(value => console.log(value))
  .catch(err => console.log(err));          1

  • 1 The expected response here is second, because the rejection happens before the resolve of promise1 resolves.

Because Promises rely on callbacks, due to their asynchronous nature, you can get into a muddle if several callbacks are nested. Finding yourself in a deeply nested callback structure is often referred to as callback hell. Promises somewhat mitigate this problem by providing a structure and making the asynchronicity explicit.

async/await

Promises have their drawbacks. They’re difficult to use in a synchronous manner, and you usually have to wade through a bunch of boilerplate code before getting to the good stuff.

async/await functions are there to simplify the behavior of using Promises synchronously. The await expression is valid only in an async function; if used outside an async function, the code throws a SyntaxError. When an async function is declared, the definition returns an AsyncFunction object. This object operates asynchronously via the JavaScript event loop and returns an implicit Promise as its result. The way the syntax is used and how it allows the code to be structured gives the impression that using async functions is much like using synchronous functions.

await

The await expression causes the execution of the async function to pause and wait until the passed Promise resolves. Then, function execution resumes.

One thing to point out is that await is not the same as Promise.then(). As await pauses the execution, causing code to execute synchronously, it isn’t chainable in the same way as Promise.then().

The next listing shows async/await in use.

Listing D.50. async/await
function resolvePromiseAfter2s () {
  return new Promise(resolve => setTimeout(() =>
  resolve('done in 2s'), 2000));
}

const resolveAnonPromise1s = () => new Promise(resolve =>
setTimeout(() => resolve('done in 1s'), 1000));


async function asyncCall () {                        1
  const result1 = await resolvePromiseAfter2s();     2
  console.log(result1);                              3
  const result2 = await resolveAnonPromise1s();      4
  console.log(result2);                              5
}

asyncCall();                                         6

  • 1 Defines an async function
  • 2 Pauses execution for 2 seconds while the Promise resolves
  • 3 result1 prints ‘done in 2s’
  • 4 Pauses execution for 1 second while the Promise resolves
  • 5 result2 prints ‘done in 1s’
  • 6 Calls the async function. This function pauses the execution for a total of 3 seconds.

You can find more details on async/await at https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/async_function.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.15.229.113