Consider the two functions below, both async, one a brutal workload that takes a lot of time, and the other one a wait function that waits a precise number of seconds by setting a timeout.
async function Brutal_Workload()
{
for(var x = 0; x< 1000 * 1000 * 1000; x )
{
}
}
async function Time_Wait(seconds)
{
var promise = new Promise((resolve, reject) =>
{
var msecs = Math.floor(seconds * 1000);
var timer =
setTimeout(
function()
{
clearTimeout(timer);
resolve();
},
msecs);
});
return promise;
}
Now, let's call the first function in a setInterval cycle
setInterval(
async function()
{
await Brutal_Workload();
console.log("BLIP");
}, 1000 / 30);
All as intended: despite the interval running at 30 calls per second, I only get 1 blip per second, because Brutal_Workload is choking it.
But when I use the other function...
setInterval(
async function()
{
await Time_Wait(1);
console.log("BLIP");
}, 1000 / 30);
I get 30 BLIPs per second. The Time_Wait function, which works otherwise just fine outside of setInterval, doesn't seem to work here.
Any idea of what might cause this behavior?
CodePudding user response:
Ok, rather than continue the back-and-forth in the comments, I'm just going to post this as an answer.
Javascript is both single-threaded and concurrent. I know you know this, but you don't seem to realize the implications. In your first function, you only see a console.log
every so often, because your "brutal workload" blocks the only thread of execution until it completes, which means that regardless of what number you passed to setInterval
not only is no other invocation running, the next bit of work isn't even being queued to run because your brutal workload is blocking the only thread of execution.
Understand, the runtime environment's setInterval
runs on the same (only) thread as your code, the JS engine doesn't cheat and run your stuff in one thread and setInterval
in another. So while brutal workload is doing its thing, setInterval
itself, much less the function you passed to it, is not running at all. Using async
and wrapping your brutal workload in a Promise makes essentially zero difference in terms of our discussion here, because brutal workload dominates.
So that explains the first example, so far so good. On to the second.
Unlike the first example, in the second there is no long-running chunk of code to tie up the thread of execution. So your callback to setInterval
runs, dutifully registers a thing to run in a second, and yields control of the thread of execution, something that again the first example does not (and cannot do). Here the Promise and async/await
actually does enable concurrency which it can't do in the first example because brutal workload is hogging the thread. So in a fraction of a second your callback to setInterval
runs again, dutifully queues up another thing to run after a second has passed, and so on.
So after ~1 second that first queued up log happens, and then after a fraction of a second after that the second, and then so on. This doesn't happen in the first example because although you told setInterval
to run 30x/sec brutal workload means that setInterval
itself can't run to even queue your callback to be ran.