0

As far as I understand, an async function allows to execute code asynchronously but it doesn't seem to work in my code :

async function longAsyncWork() {
    await foo(); // takes about 10 seconds to complete
}

console.log('start');
longAsyncWork();
console.log('end');

The 'end' is written in the console after the longAsyncWork has completed but I thought that the goal of async functions was to allow to execute code without blocking the main execution.

Maybe it's a particularity of the console.log call that prevent me from seeing the truth? Can I assume that the code after the longAsyncWork call will be executed before the completion of the longAsyncWork function?

13
  • 1
    What is the content of longAsyncWork? It sounds like the work is not actually asynchronous? Commented Jun 18, 2019 at 20:25
  • There's an await in the function Commented Jun 18, 2019 at 20:30
  • How do you execute this code? Node/browser? If you compile, then how? Commented Jun 18, 2019 at 20:30
  • Are you sure that the foo function is doing async work? Commented Jun 18, 2019 at 20:31
  • Does foo do expensive synchronous processing before it resolves? Commented Jun 18, 2019 at 20:31

3 Answers 3

1

Asnyc functions work asynchronously but javascript is single-threaded, which means the browser can do only one task at a time. What you do inside the function must be asynchronous by nature to actually make it work, however you probably did some blocking operation there that is synchronous by nature. Let me explain in more details by giving example:

async function fooThatCanBeAsync(){
    var result = await fetch("https://stackoverflow.com");
      console.log("after async task 1");
      return result;
    }
    async function fooThatCannotBeAsync(){
        var x = 0;
        for(i=0; i<10000000; i++){
            x += i;
        }
      console.log("after async task 2");
   }
   fooThatCanBeAsync();
   fooThatCannotBeAsync();
   console.log("end...");

The above silly example has two async functions. However, only one of them can run async! The first function tries to fetch something by sending http request and it will take some time. This means, cpu is now free until it starts receiving the result. By making this function async, you can now use this time in between to do other tasks. This is how concurrency works in javascript.

The second function does a for loop, making some calculations. Those calculations cannot be async, because there is no way to interrupt it, no time to wait in between, and no i/o operation. Just because you add async keyword, you can't change the fact that only one cpu thread is executing that code.

Sign up to request clarification or add additional context in comments.

Comments

0

Simply making a function async does not mean that expensive processing inside that function will not block the main thread - rather, it'll mean that the function automatically returns a Promise, and that await can be used inside of it (among other things). Any synchronous work that the function does (including any synchronous work done by await calls inside the function) will still block the main thread.

const foo = () => new Promise((resolve) => {
  for (let i = 0; i < 1e9; i++) {
  }
  console.log('foo expensive sync work done');
  resolve();
});
async function longAsyncWork() {
    await foo(); // takes about 10 seconds to complete
}

console.log('start');
longAsyncWork();
console.log('end, still on main thread');

The final Promise that the call of longAsyncWork resolves to (or any awaits inside) will not resolve until after the main thread is finished, but synchronous processing before any awaits have resolved will still block.

If you want to ensure that expensive processing doesn't block the main thread, you can use a web worker instead:

const workerFn = () => {
  self.onmessage = () => {
    for (let i = 0; i < 1e9; i++) {
    }
    self.postMessage('done');
  }
};
const workerFnStr = `(${workerFn})();`;
const blob = new Blob([workerFnStr], { type: 'text/javascript' });
const worker = new Worker(window.URL.createObjectURL(blob));
worker.onmessage = (result) => {
  console.log('result:', result.data);
};

console.log('about to post message');
worker.postMessage('arg');
console.log('main thread continuing here');

You can also call Promise.resolve() before running longAsyncWork, but unlike the worker version, this will still block the thread of the parent window (though, it will be after the main thread has finished). This is often undesirable because it can prevent the user from interacting with the page - a web worker is often a better choice.

const foo = () => new Promise((resolve) => {
  for (let i = 0; i < 1e10; i++) {
  }
  console.log('foo expensive sync work done');
  resolve();
});
async function longAsyncWork() {
    await foo(); // takes about 10 seconds to complete
}

console.log('start');
Promise.resolve()
  .then(longAsyncWork);
console.log('end, still on main thread');
<p>Page won't display for a while, or if page is already displayed, scrolling won't work, because main thread is blocked</p>

4 Comments

You can also just use setTimeout() to run the code asynchronously.
@Barmar Sure, but the only code that can result in OP's situation is if there is expensive synchronous blocking. His issue wouldn't be reproducible with setTimeout alone
Right, I see your point. When the setTimeout runs the function, it will block the UI.
it will be great if you can add the explanations in comments to your answer. It will help others alot. I mean why if longAsync work does cpu intensive sync work it will wait.
0

A working version of your code.

const sleep = ms => new Promise(r => setTimeout(r, ms))

async function longAsyncWork() {
  // await resolves the sleep promise, so it lock longAsyncWork.
  await sleep(1000)
  console.log('async')
}

console.log('start');

// async longAsyncWork is a promise, so if you doesn't lock it with await, it will run async.
longAsyncWork();
console.log('end');	

press Run code snippet to see it working.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.