Jump to content

vissha

Recommended Posts

Async/Await Arrive in Firefox

 

The new async and await keywords—which make asynchronous code more concise, obvious, and maintainable—have arrived in Firefox 52. Currently available in the latest Developer Edition release, Firefox 52 is scheduled for general release in March 2017.

 

JavaScript owes its excellent single-threaded performance and responsiveness on the web to its pervasively asynchronous design. Unfortunately, that same design gives rise to “callback hell,” where sequential calls to asynchronous functions require deeply nested, hard-to-manage code, as seen in this slightly contrived example using the localforage library:

function foo(callback) {
  localforage.setItem('x',  Math.random(), function(err) {
    if (err) {
      console.error("Something went wrong:", err);
    } else {
      localforage.getItem('x', function(err, value) {
        if (err) {
          console.error("Something went wrong:", err);
        } else {
          console.log("The random number is:", value);
        }

        if (callback) {
          callback();
        }
      });
    }
  });
}

foo(function() { console.log("Done!"); });

If you glossed over that code, or didn’t immediately understand what it did, that’s the problem.

 

ES2015 began addressing this challenge by standardizing on Promises for chained, asynchronous functions. Since their introduction, Promises have become an integral part of new web standards, including fetch and service workers. They make it possible to rewrite the previous example as:

function foo() {
  return localforage.setItem('x', Math.random())
         .then(() => localforage.getItem('x'))
         .then((value) => console.log("The random number is:", value))
         .catch((err) => console.error("Something went wrong:", err));
}

foo().then(() => console.log("Done!"));

Thanks to Promises, the code doesn’t nest deeper with each successive call, and all of the error handling can be consolidated into a single case at the end of the chain.

 

Note that in the example above, foo() returns immediately, before localforage does its work. Because foo() itself returns a Promise, future callbacks can be scheduled for after it completes with the .then() method.Semantically, the example above is much more straightforward, but syntactically, there’s still a lot to read and understand. The new async and await keywords are syntactic sugar on top of Promises to help make Promises more manageable:

async function foo() {
  try {
    await localforage.setItem('x', Math.random());
    let value = await localforage.getItem('x');
    console.log("The random number is:", value);
  } catch (err) {
    console.error("Something went wrong:", err);
  }
}

foo().then(() => console.log("Done!"));

The code above is functionally identical to the previous example, but it is much easier to understand and maintain, since the function body now resembles a common, synchronous function.

 

Functions marked async always return Promises, and thus calls to .then() work on their return value to schedule callbacks. Expressions prefixed with await effectively pause functions until the expression resolves. If an awaited expression encounters an error, then execution passes to the catch block. If uncaught, the returned Promise settles into a rejected state.

 

Similarly, instead of handling errors inside async functions, it’s possible to use normal .catch() methods on the return value instead:

async function foo() {
    await localforage.setItem('x', Math.random());
    let value = await localforage.getItem('x');
    console.log("The random number is:", value);
}

foo().catch(err => console.error("Something went wrong:", err))
     .then(() => console.log("Done!"));

For a more practical example, consider a function you might write to unsubscribe a user from web push notifications:

function unsubscribe() {
  return navigator.serviceWorker.ready
         .then(reg => reg.pushManager.getSubscription())
         .then(subscription => subscription.unsubscribe())
         .then(success => {
           if (!success) {
             throw "unsubscribe not successful";
           }
         });
}

With async and await, it becomes:

async function unsubscribe() {
  let reg = await navigator.serviceWorker.ready;
  let subscription = await reg.pushManager.getSubscription();
  let success = await subscription.unsubscribe();
  if (!success) {
    throw "unsubscribe not successful";
  }
}

Both function identically, but the latter example hides the complexities of Promises, and turns asynchronous code into code that reads (and executes) like synchronous code: from top to bottom, waiting for each line of code to fully resolve before moving on to the next line.

 

Native cross-browser support for async and await keywords is still nascent, but you can use them today with the help of a JavaScript transpiler like Babel, which can convert async / await to functionally equivalent, backward-compatible code.

 

To learn more about the async and await keywords, or Promises in general, check out the following resources:

Remember, async and await are just helpers for Promises: you can mix and match either syntax, and everything you learn about Promises applies directly to async and await.

 

Special thanks to Jamund Ferguson for suggesting improvements to the code samples in this post.

 

Source

Link to comment
Share on other sites


  • Views 691
  • Created
  • Last Reply

Archived

This topic is now archived and is closed to further replies.

  • Recently Browsing   0 members

    • No registered users viewing this page.
×
×
  • Create New...