In Javascript, async functions are only asynchronous, all code still executes on a single thread. (The exceptions to this is when you use something like threads in Node or web workers in the browser, see the links for more info on that.)
To illustrate what this means, let's define two sample functions:
function halts() {
const start = Date.now()
while (Date.now() < start + 2000);
}
function resolves() {
return new Promise((resolve) => setTimeout(resolve, 2000));
}
The first function will block the thread for roughly 2 seconds, returning nothing, the second function will return a promise that will resolve after a similar period of time.
When you're using these functions, the crucial difference is that when calling the first one, all execution blocks up until the function completes, since everything is running on a single thread:
function halts() {
const start = Date.now()
while (Date.now() < start + 2000);
}
const before = Date.now();
halts();
const after = Date.now();
console.log(`execution halted for ${after - before}ms`);
The difference when using asynchronous operations (regardless of whether you use promises or async-await for the implementation) is that you can allow the main thread to take on other work until you wait, for example, for a network request to complete.
function resolves() {
return new Promise((resolve) => setTimeout(resolve, 2000));
}
const interval = setInterval(() => {
console.log('ping');
}, 500);
const before = Date.now();
resolves().then(() => {
clearInterval(interval);
console.log('promise resolved');
});
const after = Date.now();
console.log(`execution halted for ${after - before}ms`);
Notice how the second example continues with the execution of the rest of the code immediately, instead of halting everything. The async operation isn't done on a separate thread, it's simply deferred for later on the same thread.
For a more technical explanation, Deceze's answer is a good short summary, this video by Jake Archibald is a good general overview as well.