Is async await truly non-blocking in the browser?

JavascriptUser InterfaceAsynchronousAsync AwaitEcmascript 2017

Javascript Problem Overview


I have been playing around with the feature in an SPA using TypeScript and native Promises, and I notice that even if I refactor a long-running function into an async function returning a promise, the UI is still unresponsive.

So my questions are:

  • How exactly does the new async/await feature help avoid blocking the UI in the browser? Are there any special extra steps one needs to take when using async/await to actually get a responsive UI?

  • Can someone create a fiddle to demonstrate the how async/await helps to make the UI responsive?

  • How does async/await relate to prior async features such as setTimeout and XmlHttpRequest?

Javascript Solutions


Solution 1 - Javascript

await p schedules execution of the rest of your function when promise p resolves. That's all.

async lets you use await. That's (almost) all it does (It also wraps your result in a promise).

Together they make non-blocking code read like simpler blocking code. They don't unblock code.

For a responsive UI, offload CPU-intensive work to a worker thread, and pass messages to it:

async function brutePrime(n) {
  function work({data}) {
    while (true) {
      let d = 2;
      for (; d < data; d++) {
        if (data % d == 0) break;
      }
      if (d == data) return self.postMessage(data);
      data++;
    }
  }

  let b = new Blob(["onmessage =" + work.toString()], {type: "text/javascript"});
  let worker = new Worker(URL.createObjectURL(b));
  worker.postMessage(n); 
  return await new Promise(resolve => worker.onmessage = e => resolve(e.data));
}

(async () => {
  let n = 700000000;
  for (let i = 0; i < 10; i++) {
    console.log(n = await brutePrime(n + 1));
  }
})().catch(e => console.log(e));

Solution 2 - Javascript

async is a more elegant way to structure asynchronous code. It doesn't allow any new capabilities; it's just a better syntax than callbacks or promises.

So, async can't be used to "make something asynchronous". If you have code that has to do lots of CPU-based processing, async isn't going to magically make the UI responsive. What you'd need to do is use something like web workers, which are the proper tool to push CPU-bound work to a background thread in order to make the UI responsive.

Solution 3 - Javascript

JavaScript is single-threaded and runs in the same thread as the UI. So all JavaScript code will block the UI. As mentioned by others web workers can be used to run code in other threads, but they have limitations.

The difference between async functions and regular ones is that they return a promise. Using a callback you can then defer the execution of code, that handles the result of a function invocation and thereby allowing the UI to do some work. The following three examples have the same effect:

async function foo() {
  console.log("hi");
  return 1; 
}
foo().then(result => console.log(result))
console.log("lo");

function foo() {
  console.log("hi");
  return 1; 
}
Promise.resolve(foo()).then(result => console.log(result))
console.log("lo");

function foo() {
  console.log("hi");
  return 1; 
}
const result = foo();
setTimeout(() => console.log(result));
console.log("lo");

In all three cases the console logs hi, lo, 1. Before 1 is printed the UI can handle user input or draw updates. The reason 1 that is printed last in the first two cases is that callbacks for promises are not executed immediately.

await allows you to do that without callbacks:

async function foo() {
  console.log("hi");
  return 1; 
}

async function bar() {
  const result = await foo();
  console.log(result);
}

bar();
console.log("lo"); 

That will also print hi, lo, 1. Much like a callback for a promise, the code after await is never executed immediately.

Solution 4 - Javascript

It's clear from the description on developer.mozilla.org that it's non-blocking:

> The await keyword causes the JavaScript runtime to pause your code on > this line, allowing other code to execute in the meantime (Note: my bold), until the > async function call has returned its result. Once that's complete, > your code continues to execute starting on the next line.

Solution 5 - Javascript

I'm late to the party here. I wanted to verify both the synchronous and asynchronous use cases.

To be clear, async/await does not create synchronous code. It only adds syntactic sugar that makes the code look synchronous. Under the wrappers, the Promise logic continues to implement non-preemptive multitasking.

In the example gist you can run the example with a command line parameter that selects blocking or non-blocking CLI input. asyncExample.js

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionprmphView Question on Stackoverflow
Solution 1 - JavascriptjibView Answer on Stackoverflow
Solution 2 - JavascriptStephen ClearyView Answer on Stackoverflow
Solution 3 - Javascripta better oliverView Answer on Stackoverflow
Solution 4 - JavascriptPer Quested AronssonView Answer on Stackoverflow
Solution 5 - JavascriptGreg SmithView Answer on Stackoverflow