This question already has answers here:
How can I pass a parameter to a setTimeout() callback?
(29 answers)
Closed 3 years ago.
Example of setTimeout being executed after a synchronous code is below it
console.log('hello');
setTimeout(() => console.log('timeout'), 0);
console.log('end');
Console Output:
hello
end
timeout
Async:
function asyncForEach(array, callBack) {
array.forEach(x => setTimeout(callBack(x), 0));
}
asyncForEach([1,2,3,4], (i) => console.log(i));
console.log('end');
Console Output:
1
2
3
4
end
Synchronous:
[1,2,3,4].forEach(x => console.log(x));
console.log('end');
Console output:
1
2
3
4
end
I am trying to better understand the event loop and the task queue, and after watching a well recommended video (https://www.youtube.com/watch?v=8aGhZQkoFbQ) came across the semi statement of: "Callbacks can be synchronous and asynchronous". The speaker went on to demonstrate how the synchronous way would not end up going through the task queue, and therefore the event loop, so everything remained on the stack. However, the asynchronous way would cause the task queue to be populated with the eventual return of the setTimeout callback, which would make any synchronous below code executable since the event loop has to wait for an empty stack to insert the returned callback.
When running the above code (something I stole from him and also edited, so it could be a wrong addition on my part), the asynchronous way produces the same results as the synchronous way.
Could someone help explain why the asynchronous way does not act as the very first setTimeout example and/or provide explanation of how regular callbacks (such as the array helper methods) are not inserted into the task queue therefore never messed with by the event loop?
Your "asynchronous way" isn't asynchronous, because you called the function and passed its return value to setTimeout:
function asyncForEach(array, callBack) {
array.forEach(x => setTimeout(callBack(x), 0)); // <-- callBack(x) calls callBack immediately
}
asyncForEach([1,2,3,4], (i) => console.log(i));
console.log('end');
If you want it delayed, make a function to pass to setTimeout that it can call later:
function asyncForEach(array, callBack) {
array.forEach(x => setTimeout(() => callBack(x), 0)); // <-- Makes closure that calls callBack(x) later
}
asyncForEach([1,2,3,4], (i) => console.log(i));
console.log('end');
There's a bug in your asyncForEach function. You're calling callBack(x) immediately and then passing the result of that function (undefined) to setTimeout.
Related
This question already has answers here:
Are JavaScript Promise asynchronous?
(3 answers)
When does async function actually return a pending promise?
(1 answer)
Do Javascript promises block the stack
(4 answers)
What are asynchronous functions in JavaScript? What is "async" and "await" in JavaScript?
(2 answers)
Closed last year.
Every single example I read about it has code demonstrations that are synchronously executed, with the article writers pretending they're asynchronous.
No example I've seen yet says the code keeps running past the 'asynchronous' promise. If the code kept running while the promise was fulfilled, or the await was taking place, then it could be called asynchonous.
But if the main line of code stops for the promise/await response, then it's simply synchronous.
Can anyone explain this? I had the impression that code ran past the promise/sync function, rather than stopping and 'awaiting' the result to then continue the rest of the code identically to a normal synchronous execution operation.
Promises themselves aren't really asynchronous. They are a pattern to implement working with other things that themselves are asynchronous.
Just like a callback is just a function, we tend to call them 'callbacks' if they get triggered at a later point.
All a Promise really is for is encapsulating the status of an asynchronous operation and let people access the result.
Asterisk: Promises themselves are also a little asynchronous, because they guarantee that the function passed to then() always happens at least in the next tick if the result already arrived.
And await doesn't block the process, it literally pauses the function and lets other things run. Similar to yield, which is also not asynchronous by itself.
Promises register callbacks for further execution when they resolve:
console.log('Beginning');
const test = new Promise(resolve => setTimeout(resolve, 1000));
test.then(() => console.log('Resolved'));
console.log('End');
Now if you think this is synchronous code:
(async function() {
console.log('Beginning');
const test = new Promise(resolve => setTimeout(resolve, 1000));
await test;
console.log('Resolved');
console.log('End');
})();
Well it's not, it's actually an equivalent of:
console.log('Beginning');
const test = new Promise(resolve => setTimeout(resolve, 1000));
test.then(() => {
console.log('Resolved');
console.log('End');
});
Conclusion: when you await a Promise you just use syntactic sugar. Really nice syntactic sugar that prevents callback hell:
The call stack unwinds and frees the event loop with async/await, just like it would with callbacks, making the event loop available for other code execution until the Promise resolves.
This question already has answers here:
Why is the method executed immediately when I use setTimeout?
(8 answers)
Closed 1 year ago.
const eventLoop = () => {
console.log("Start");
setTimeout(() => console.log("Settimeout"), 0)
console.log("End");
}
eventLoop();
Output:
Start
End
Settimeout
But for
const eventLoop = () => {
console.log("Start");
setTimeout(console.log("Settimeout"), 0)
console.log("End");
}
eventLoop();
Output:
Start
Settimeout
End
Please help me to understand JS execution stack and event loop in above scenario
Have you executed console.log() inside developer tools?
I think it was very confusing at first time. it has many relationship with this question.
console.log("Hello, World!");
// returns undefined
It's a kind of expression returns undefined with side effect.
const eventLoop = () => {
console.log("Start");
setTimeout(console.log("Settimeout"), 0)
console.log("End");
}
eventLoop();
console.log("Settimeout") expression in above code is executed immediately, before setTimeout function called.
Therefore, it is not related with event loop.
const eventLoop = () => {
console.log("Start");
setTimeout(() => console.log("Settimeout"), 0)
console.log("End");
}
eventLoop();
In this example you :
console.log is put on function call stack, since stack is empty it is executed.
The function passed to setTimeout is added to the tasks queue. The thing to note here is that the delay will be atleast 0ms and it is not guaranteed that the function will be called after 0ms.
Now this console.log is called using the stack.
Now, since the stack is empty and the execution has completed items from the queue are picked. So the function passed in step 2 is picked and called.
const eventLoop = () => {
console.log("Start");
setTimeout(console.log("Settimeout"), 0)
console.log("End");
}
eventLoop();
In this example, step 1 and 3 are exactly same as example#1. But, in step 2 you are not passing any function inside setTimeout you are simply calling console.log() there.
() => console.log("Settimeout") is not the same as console.log("Settimeout").
First becomes ->
() => {
return console.log("Settimeout");
}
Second is simply, console.log("Settimeout").
In short, when in second example execution reaches line 2, console.log() is executed. But in first example, a function is passed to setTimeout which has the responsibility of calling console.log().
The following URL explains very well the event loop.
https://nodejs.org/en/docs/guides/event-loop-timers-and-nexttick/
This one explains what parameters receives the SetTimeOut function
https://www.w3schools.com/jsref/met_win_settimeout.asp
When you use the function in the first of your cases, the function passed as parameter is called after the next cycle in the event loop, it means you have to pass a function to be called after the cycle, but if you put "console.log" you are performing that function instantly, so there is no function to be called in the next cycle.
According to this stackoverflow answer,
functions passed as parameters are always callbacks, even if the intention is that the function is called synchronously...
and I've learned the event loop mechanism which basically says that call back functions are push into a waiting queue and executed after synchronous code finishes(stack empty), however in the following code
function myfun(a, b, callback) {
console.log(a);
callback();
console.log(b);
}
function cb() {console.log('hi')}
myfun(1, 2, cb) // result: 1 hi 2
the result is not 1 2 hi as I expected, from which I infer that only callback functions that fire some 'event signal' like setTimeout will be pushed into such queue but I can't find concrete reference to support it?
"Callbacks" are usually used in conjunction with asynchronous processes like ajax requests or event handlers attached to the ui. We call them "callbacks" in these cases since they need to be executed after something else and it is clear where the program's logic will pick back up this "call" after the async process is complete or triggered brings us "back" here.
Using setTimeout() you can add to the event loop stack. Using a promise you can invoke the stack on the event loop as you wait for an asynchronous process to finish.
Your code doesn't do anything to interrupt the synchronous flow of the code. This snippet shows how even though we have added a timeout of 0 which should delay the action, we can await a promise to allow the event loop's stack to run.
function myfun(a, b, callback) {
console.log(a);
callback();
console.log(b);
}
function cb() {
console.log('hi')
}
myfun(1, 2, cb) // result: 1 hi 2
// same behavior as above
function myStaticFun() {
console.log(1);
cb();
console.log(2);
}
myStaticFun();
// now same as before using a promise to wait a moment and the event loop stack is invoked during the pause
function sleep(ms) {
return new Promise(resolve => setTimeout(resolve, ms));
}
async function myEventLoopInvokingFun(a, b, callback) {
console.log(a);
setTimeout(callback, 0)
await sleep(0);
console.log(b);
}
myEventLoopInvokingFun(1, 2, cb);
It does not necessarily mean that every a callback is asynchronous and must be put into a some task queue and to be executed once synchronous code pieces (call stack is empty) finishes. The callback functions dealing with Promises are candidates for task queue. For instance in your case; cb function simply runs in a synchronous manner; so that the result is 1 hi 2 as you indicated; however if we modify your code as follows:
function myfun(a, b, callback) {
console.log(a);
window.setTimeout(callback, 0);
console.log(b);
}
function cb() {
console.log('hi');
}
myfun(1, 2, cb) // result: 1 2 hi
this will result in 1 2 hi. Even though I set the timeout to just 0 milliseconds; cb function will output after the second console.log within myfun function. I would recommend you to take a look at MDN and this good explanation of call stack, event loop, and task queues. Hope this helps.
I have the following code that uses fetch. From what I understand, the callback function will not be invoked until the promise is fulfilled. Because of that, I was expecting the callback functions to be executed in the middle of processing other things (such as the for loop). However, it is not doing what I expect. My code is as follows:
console.log("Before fetch")
fetch('https://example.com/data')
.then(function(response){
console.log("In first then")
return response.json()
})
.then(function(json){
console.log("In second then")
console.log(json)
})
.catch(function(error){
console.log("An error has occured")
console.log(error)
})
console.log("After fetch")
for(let i = 0; i < 1000000; i++){
if (i % 10000 == 0)
console.log(i)
}
console.log("The End")
Rather than the callback being immediately run when the promise is fulfilled, it seems to wait until all the rest of my code is processed before the callback function is activated. Why is this?
The output of my code looks like this:
Before fetch
After fetch
0
10000
.
.
.
970000
980000
990000
The End
In first then
In second then
However, I was expecting the last two lines to appear somewhere prior to this point. What is going on here and how can I change my code so that it reflects when the promise is actually fulfilled?
The key here is that the for loop you're running afterwards is a long, synchronous block of code. That is the reason why synchronous APIs are deprecated / not recommended in JavaScript, as they block all asynchronous callbacks from executing until completion. JavaScript is not multithreaded, and it does not have concepts like interrupts in C, so if the thread is executing a large loop, nothing else will have the chance to run until that loop is finished.
In Node.js, the child_process API allows you to run daemon processes, and the Web Worker API for browsers allows concurrent processes to run in parallel, both of these using serialized event-based messaging to communicate between threads, but aside from that, everything in the above paragraph applies universally to JavaScript.
In general, a possible solution to breaking up long synchronous processes like the one you have there is batching. Using promises, you could rewrite the for loop like this:
(async () => {
for(let i = 0; i < 100000; i++){
if (i % 10000 == 0) {
console.log(i);
// release control for minimum of 4 ms
await new Promise(resolve => { setTimeout(resolve, 0); });
}
}
})().then(() => {
console.log("The End");
});
setTimeout(() => { console.log('Can interrupt loop'); }, 1);
Reason for 4ms minimum: https://developer.mozilla.org/en-US/docs/Web/API/WindowOrWorkerGlobalScope/setTimeout#Reasons_for_delays_longer_than_specified
No matter how fast is your promise fulfilled. Callbacks added to Event Loop and will invoke after all synchronous tasks are finished. In this example synchronous task is for loop. You can try event with setTimeout with 0ms, it will also work after loop. Remember JS is single-threaded and doesn't support parallel tasks.
Referance:
https://developer.mozilla.org/en-US/docs/Web/JavaScript/EventLoop
There's no guarantee when the callback will execute. The for-loop requires very little processing time, so it's possible that the JS engine just decided to wait until it's over to complete the callback functions. There's no certain way of forcing a specific order of functions either unless you chain them as callbacks.
This question already has answers here:
I know that callback function runs asynchronously, but why?
(3 answers)
Closed 6 years ago.
I started to develop in node.js just a while ago. Lately, I did some deep dive into the 'event loop' and async mechanism of node. But still I'm not fully understand the different between sync and async callbacks.
In this example from node.js API, I understand why is not clear which function will be called first.
maybeSync(true, () => {
foo();
});
bar();
But, what if we had:
syncOrAsync(arg, () => {
if (arg) {
cb(arg);
return;
}
});
syncOrAsync(true, function(result) {
console.log('result');
});
console.log('after result);
It's not clear to me why they are always execute in sync order, although I did a callback function which should execute by the event loop after the stack is empty ( console.log('after result') was finished ). Do I always need to add process.nextTick(cb); to get async? And what is the diffrent between process.nextTick and setTimeout();?
Unless you have something that is actually async, like timers or external calls etc. the code will always be synchronous, as that's the default state of all javascript code.
Adding a callback doesn't make it asynchronous
Here's an example of asynchronous code
function syncOrAsync(sync, cb) {
if (sync) {
return cb();
} else {
setTimeout(cb, 100); // async, waits 0.1 seconds to call callback
}
}
syncOrAsync(true, function(result) { // synchronous call
console.log('result 1'); // happens first
});
syncOrAsync(false, function(result) { // asynchronous call
console.log('result 2'); // happens last, as it's async
});
console.log('result 3'); // happens second
Using process.nextTick() doesn't really make the functions asynchronous, but it does do somewhat the same
function syncOrAsync() {
console.log('result 1'); // this happens last
}
process.nextTick(syncOrAsync);
console.log('result 2'); // this happens first
This would defer the execution of syncOrAsync until the next pass around the event loop, so it would be in many ways the same as setTimeout(syncOrAsync), but the function still wouldn't be asynchronous, the callback would execute immediately, we've just delay the entire execution of the function.