I'm attempting to get a better grasp on async functions and promises in JS. To do this, I wrote a sample program that has the goal of calling a function that does busy work (purposefully not using async setTimeout
as I want to mimic a long-running process) but returns immediately. However, I can't seem to figure out why this isn't working.
test();
async function intense(){
var start = new Date().getTime();
for (var i = 0; i < 1e6; i++) {
if ((new Date().getTime() - start) > 2000){
break;
}
}
console.log("Done with async work");
}
async function test(){
console.log("Print 1");
intense(); // does some busy work for a few seconds
console.log("Print 2"); // want this to print immediately after print 1
}
When I run it, I get:
Print 1
Done with async work
Print 2
And I would like it to be:
Print 1
Print 2
Done with async work
I thought that it would print the latter sequence because I declared the function intense()
to be async, so it would return a promise immediately and continue work asynchronously.
I even tried to refactor the intense
function to be a promise that resolves immediately, but to no avail.
async function intense(){
return new Promise((resolve)=> {
resolve();
var start = new Date().getTime();
for (var i = 0; i < 1e6; i++) {
if ((new Date().getTime() - start) > 2000){
break;
}
}
console.log("Done with async work");
}, null)
}
What am I missing?
There a are a couple of reasons for what you're seeing:
An async
function is synchronous up until its first await
or return
, so the entire function runs before returning in your case.
Busy-waiting isn't asynchronous.
test
needs to use await
if it's going to wait for intense
to complete before continuing.
Moving something into a promise doesn't take it off the thread. The only way to do that in most JavaScript environments (including browsers) is to use a Worker
thread (MDN, Node.js docs — Node.js has had Worker
since ~v10.5, and while it's still marked "experimental" the main parts of the API should be fairly stable as they're drawn from the web worker standard API).
It's important to remember that promises don't make anything asynchronous¹, they provide a means of observing the result of something that's already asynchronous.
Here's an example using setTimeout
for the asynchronous part that help you understand these better:
function delay(ms) {
return new Promise(resolve => setTimeout(resolve, ms));
}
async function intense(value) {
console.log("intense(" + value + ") - This is synchronous");
await delay(100);
console.log("intense(" + value + ") - This is asynchronous, because it's after `await`");
}
async function test(){
console.log("Print 1");
intense(1); // <== WITHOUT await
console.log("Print 2");
await intense(2); // <== With await
console.log("Print 3");
}
test();
.as-console-wrapper {
max-height: 100% !important;
}
¹ There's one small caveat to that: The handler you pass to then
, catch
, or finally
will always be called asynchronously, even if the promise you're calling them on is already settled. That's literally the only thing that promises actually make asynchronous.
so it would return a promise immediately and continue work asynchronously.
No it would not. The callback passed to the Promise constructor is called immeadiately. What is async is the process of calling resolve
or reject
later on, and how .then
chains get called back somewhen.
However it isnt async in the sense that the code runs on another thread or gets deferred, that won't happen as JS itself is executed in a single thread*.
console.log(1);
const promise = new Promise((resolve, reject) => {
console.log(2); // gets executed immeadiately
});
promise.then(() => console.log(4)); // < Promise resolve asynchronously
console.log(3);
*If you plan to do really "intense" work, it might be benefitial to do that in another thread (see WebWorker
in browsers and child_process.spawn
for NodeJS).
This is a misunderstanding that is pretty easy to make with javascript. Your function intense()
blocks the thread. Putting something in an async function does not change the fact that you only have one thread in javascript. As soon as the interpreted starts running that for
loop it's going to use the one thread to run it until it's over. Nothing else will happen until then.
Async functions don't return immediately, they run the body of the code until the hit an await and return a promise. In your example, the entire function will run before it returns.
You can't use this kind of long running process without blocking. That's why node goes out of its way to offload things like i/o access and timers to another thread.
See here for more details: https://nodejs.org/en/docs/guides/event-loop-timers-and-nexttick/
If you want to run some long-running code like this asynchronously you will need to spawn a child process: https://nodejs.org/api/child_process.html
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With