简体   繁体   中英

Clarification on synchronous and asynchronous callbacks in javascript

iam currently working through the concept of callbacks and asynchronous programming in javascript. For that i read the corresponding chapters in the book "JavaScript" written by Philip Ackermann (ISBN:937-3-8362-5696-4). But i have problems understanding the term asynchronous applied to callbacks used in examples of the book.

My current understand is that i can write synchronous callbacks like this:

function synchronousCallback(text, callback) {
    //other code
    callback(text);
}
synchronousCallback("End of function", console.log);

In the above example the callback is in my opinion only a nested function call. Nothing more. But in a similar example of the book the author calls such a function asynchronous. See below the exact example of the book:

function asyncFunction(callbackFunction) {
    //some code
    console.log('Before callback');
    callbackFunction();
    console.log('After callback');
    //some more code
}
function callbackFunction() {
    console.log('called callback');
}
asyncFunction(callbackFunction);

My understanding of the code execution is that this callback would be executed as soon as the 'other code' is finished. The callback would not be added to the callback queue of the javascript engine and therefore be synchronous/blocking.

In my point of view a callback is asynchronous when used with setTimeout() or setInterval() .

Maybe the example in the book is misleading or i misunderstood the term asynchronous in that case or i didnt not understand the execution order of such a callback scenario correctly.

Thanks for any help or clarification

There's nothing asynchronous about the example you gave.

Callbacks are asynchronous if the JavaScript event loop carries on running the rest of your program until an external factor (time passing in the case of setTimeout ) triggers the callback.

Either the book is wrong, or you've not adequately expressed everything in the "similar" example it gave.

I have already mentioned the difference between synchronous and asynchronous behavior in JavaScript with this answer to your other question. I'll try to give you more details with this answer.

There, I recommended that you watch couple of talks about the subject. A talk by Philip Roberts, another talk by Jake Archibald or Jake's blog which explains the same. I'll try to summarize all of that.

All JavaScript code is synchronous and executes within a single thread. This means that there's one call stack and it can do one thing at a time. To better understand JavaScript runtime, please take a look at this image taken from MDN .

事件循环

Let's try to go through your second example to see what's going on. When asyncFunction() gets called, it is pushed to stack (Jake calls them tasks, but based on MDN image, they are frames). Then, console.log('Before callback') is called and it gets pushed to stack on top of the current frame. So now, there's console.log on top and asyncFunction below.

console.log logs that string to console. Then, it's removed (popped) of the stack. asyncFunction is now on top of the stack. Now, callbackFunction() gets called and it is pushed to the stack, which then calls console.log('called callback') which also gets pushed to the stack.

Now, there are three functions on the stack: asyncFunction at the bottom, callbackFunction and console.log at top. When console.log finishes its job, it gets popped off the stack and now callbackFunction is also finished and that one also gets popped off the stack. Now, console.log('After callback') is called, pushed to the stack and popped after execution, which means that asyncFunction is finished and can be popped off the stack.

This is where all ends, the stack is empty, no more frames on it. Based on this and talks from the link above, there's nothing asynchronous in this example. A step by step is made by JS runtime and no asynchronous jumps are made here. But, how do we achieve concurrency in JS and do we need it? To quote Philip:

The reason we can do things concurrently is that the browser is more than just the runtime.

This is the reason why we can use setTimeout(() => { doSomething(); }, 5000) which would wait for 5(000 thousand milli)seconds without blocking (freezing) web page during that time. What happens when setTimeout is called? The browser starts another thread which runs in parallel . The thread's job is to only wait for 5 seconds. But, now it gets interesting what happens when the time's up.

To prevent concurrent modifications which might lead to unexpected behaviors, browsers have a queue mechanism. This allows the thread created by setTimeout to post a message to it and the message is, in this case, a function passed to setTimeout that will get executed once the message is processed.

But when are the messages processed? Well, just after no frames (tasks) are stacked. This means that messages are waiting for stack to get cleared after all frames are finished so that they can be processed. Messages are processed one at a time per one loop. Once message is taken as task it becomes regular JS code which gets executed on the same thread with the same rules for pushing and popping stack. Any other potential messages that are queued in the meantime must wait for the current message/frame to be processed.

setTimeout and setInterval are all parts of WebAPI s. Many (if not all) of them have asynchronous callbacks, and some examples include: DOM events, XHR events, Fetch events, Web workers, Web sockets, Promises, MutationObserver callbacks and so on. The last two (Promises and MutationObservers) schedule tasks on a different queue (microtask queue), but it's still asynchronous.

The difference between messages that are set to microtask queue and regular (or macrotask) queue is that the messages from macrotask queues are taken one at a time per event loop (which means that the whole loop needs to go around between two messages are processed), while messages from microtask queues are taken immediately after the stack is cleared. This means that they have higher priority than the ones on the macrotask queues. For more info, please watch/read Jake's talk/blog.

In my point of view a callback is asynchronous when used with setTimeout() or setInterval().

Yes, but not only with setTimeout() or setInterval() . If you set your callback toXMLHttpRequest 's onreadystatechange function, it will be called asynchronously all the same.

Please note that there may be other APIs that require different set of function parameters than the ones from these examples - in fact, these examples aren't using function parameters at all.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM