I have a question to async, await and setTimeout(). I thought, I use asynchron functions for slow processes. So I tried it with a large loop. On my Computer, it needs few second
Promises and async
functions don't offload your code to another thread. If you want to move that long-running process off the main thread, on browsers look at web workers, and on Node.js look at child processes.
Promises and async
functions (which are just a syntax for creating and consuming promises) don't move your processing to any other thread, it still happens on the same thread you start the process on. The only thing they do is ensure that then
and catch
callbacks are called asynchronously. They don't make your code asynchronous (other than that one thing, ensuring the callbacks happen asynchronously).
So your first block using setTimeout
just sets a timeout, returns a promise, and then when the timeout expires it blocks the main thread while your slow-running process executes. This just changes when the blocking happens a little bit, it doesn't change the fact of the blocking.
You can see that effect here, notice how the counter pauses when the long-running process occurs:
function slowFunction() {
return new Promise(resolve => {
setTimeout(() => {
const stop = Date.now() + 2000;
while (Date.now() < stop) {
// busy wait (obviously, never really do this)
}
}, 1000);
});
};
console.log("before slowFunction");
slowFunction()
.then(() => {
console.log("then handler on slowFunction's promise");
})
.catch(console.error);
console.log("after slowFunction");
let counter = 0;
const timer = setInterval(() => {
console.log(++counter);
}, 100);
setTimeout(() => {
clearInterval(timer);
console.log("done");
}, 3000);
.as-console-wrapper {
max-height: 100% !important;
}
Your second block not using setTimeout
just blocks right away, because the promise executor function (the function you pass new Promise
) runs immediately and synchronously, and you're not doing anything to make it asynchronous.
You can see that here; the counter pauses right away, not later:
function slowFunction() {
return new Promise(resolve => {
const stop = Date.now() + 2000;
while (Date.now() < stop) {
// busy wait (obviously, never really do this)
}
});
};
console.log("before slowFunction");
slowFunction()
.then(() => {
console.log("then handler on slowFunction's promise");
})
.catch(console.error);
console.log("after slowFunction");
let counter = 0;
const timer = setInterval(() => {
console.log(++counter);
}, 100);
setTimeout(() => {
clearInterval(timer);
console.log("done");
}, 3000);
.as-console-wrapper {
max-height: 100% !important;
}
We don't even see the before slowFunction log appear until after the long-running code has finished, because the browser never got a chance to repaint, we had the thread hogged.
Regarding async
functions: The code in an async
function starts out synchronous, and is synchronous until the first await
(or other construct, such as setTimeout
, that schedules things to execute later). Only the code after that is asynchronous (because it had to wait).
Here's an example demonstrating that:
async function foo() {
console.log("before await");
await Promise.resolve();
console.log("after await");
}
console.log("before foo");
foo()
.then(() => {
console.log("then handler on foo's promise");
})
.catch(console.error);
console.log("after foo");
Here's the output of that:
before foo before await after foo after await then handler on foo's promise
Notice how before await occurs before after foo; it's synchronous with the call to foo
. But then after await doesn't occur until later (because await Promise.resolve()
has to make the code following it occur asynchronously; it's syntactic sugar for then
, which promises not to call its handler synchronously even if the promise is already resolved).