JavaScript Asynchronicity and Runtime

橙三吉。 提交于 2021-02-11 12:58:53

问题


I have been reading a lot about asynchronous JavaScript, but I still can't really wrap my head around it.

From what I understand, asynchronous JavaScript is just writing a handler(s) for asynchronous operation. The asynchronous operation (which is not part of JS Engine) then does all the magic of running the operation on a separate thread, moving callback into message (callback) queue and finally pushing the callback to call stack where it is executed by JS Engine.

Should I understand more about Asynchronous operations other than that they "magically" execute callback once they are completed? (by "magically" going through the process explained above)

For example, all I know is that setTimeout() function accepts is asynchronous and accepts callback and time in ms and nothing about how it actually works.

So as a JavaScript developer, should I not worry about the whole process of how JS runtime makes function asynchronous?

Will I ever have to deal with implementation of message queue or event loop themselves or will the "magic" async operations always be provided by the runtime?

Or am I misunderstanding something?


回答1:


You really need to stop thinking that everything is about threads. Almost all asynchronous operations in javascript is executed on the main thread.

Note: There will be people who tell you this is wrong, that javascript uses multiple threads and point to node.js documentation about V8. I am telling you that they are wrong. Node.js do run additional threads but they are only for disk I/O (all disk I/O are executed on one disk I/O thread, not one thread per file), DNS (because DNS APIs are blocking), crypto functions (because it uses CPU rather than I/O) and zip compression (same reason as crypto). Everything else including network I/O, mouse and keyboard handling, setTimeout etc. don't run on separate threads. You can read more about this on node's own documentation about the event loop: https://nodejs.org/en/docs/guides/dont-block-the-event-loop/

Asynchronous code in javascript mostly refer to what C programmers call non-blocking I/O. Blocking I/O stops your process until data is available:

// Blocking I/O pseudocode
data = read(file);

Non-blocking I/O returns immediately and does not return the data available. Instead it begins the process of fetching data:

// Non-blocking I/O (javascript's internal asynchronous) pseudocode
beginReading(file);

while (waiting) {
   if (readyToRead(file)) {
       data = read(file);
   }
}

The advantage of non-blocking I/O compared to blocking I/O is that while the OS tells your device driver to fetch some bytes from the file and your device driver begins a PCI transaction and the PCI bus communicates with your disk controller and your disk controller begins addressing operation on the storage medium.. while all that is happening (which is a long time in CPU time).. you can execute code:

// Blocking I/O pseudocode
data = read(file); // several million instructions could have been executed
                   // but our process is blocked waiting for data

// Non-blocking I/O (javascript's internal asynchronous) pseudocode
beginReading(file);

while (waiting) {
   if (readyToRead(file)) {
       data = read(file);
   }
   else {
       executeMoreCode(); // continue processing javascript while waiting
   }
}

In C/C++ most people would hardcode (as in, actually write the executeMoreCode() above) unless they are comfortable working with function pointers (the syntax is absolutely horrible). And even then, C/C++ does not offer an easy way to redefine that function after you've compiled your program (clever people can do wonders with interfaces - consider printer drivers which can be loaded after Windows have been compiled - but it's still complicated).

Javascript has first-class functions so most javascript API allow you to pass a callback as an argument to a function call that starts the non-blocking request.

Internally this is what javascript does:

// pseudocode:
do {
    eventHandlers = executeJavascript();
    // end of execution

    events = waitForAllIO(); // this actually blocks but it is waiting
                             // for ALL I/O instead of just one

    if (events.timeout) {
        foreach (callback from eventHandlers) {
            if (callback is TIMEOUT_HANDLER) {
                callback(events.timeout);
            }
        }
    }
    else {
        foreach (event from events) {
             foreach (callback from eventHandlers) {
                 if (callback is for event) {
                     callback(event);
                 }
             }
        }
    }
} while (eventHandlers.length > 0)

This loop goes by many names. Adobe Flash (which like node.js is an ECMAScript language that is just slightly different from browser javascript) calls it the "elastic racetrack". Most people just call it the event loop.

As you can see, nothing in the above logic requires additional threads. This architecture is so successful in handling concurrency that javascript implement threads (web workers & worker threads) as passing events back to the main thread thus even with threading javascript generally avoid locking (which is why neither web workers nor worker threads have locking mechanisms built in).

The thing to remember as a javascript programmer is:

  1. Javascript is a strictly synchronous language. All lines of code are executed sequentially in order just like C or Java or Python.

  2. Function definitions and reference are not called when defined or passed as argument to other functions just like C or Java or Python.

  3. Asynchronous code does not execute code in parallel it only waits for events in parallel. 100% of the speedup of programs written in something like node.js is due to being able to make 1000 parallel I/O requests and wait for all of them at the same time. The I/O requests will be executed by hardware eg. hard disk, routers or external processes eg. SQL servers, Google, not javascript. Thus javascript does not need to execute any parallel code to get advantages of parallelism.

  4. There is nothing magical about all this. You will face the same asynchronous behavior if you writ GUI code in C++ or Java using frameworks like GTK or WxWidgets or Swing.

I've written much more detailed explanations to specific questions on this subject. If you want to know more you may find my answers to other questions of interest to you:

Is there any other way to implement a "listening" function without an infinite while loop?

Does javascript process using an elastic racetrack algorithm

node js - what happens to incoming events during callback excution

I know that callback function runs asynchronously, but why?

Performance of NodeJS with large amount of callbacks

Is nodejs representing Reactor or Proactor design pattern?




回答2:


asynchronous can cover, events such as a user clicking on a button, timeouts where a block of code is queued to run after a specific time, intervals where a block of code is scheduled to run every certain interval and requests for data where a call to a server will respond after an uncertain time. It really is a topic larger than a StackOverflow question can tackle. But read up on event handlers, promises and if you are really up for some heavy stuff RxJs.

Most devs will never need to fully understand how the runtime enables these things, just a good understanding of how to use them.




回答3:


I agree with Adrian, that async programming is a rather bug topic to be covered in StackOverflow, so I am going to share some resources to study and get a good understanding of these concepts.

They're quite a lot, and rather heavy material, but it's worth reading/watching as asynchronicity is one of the fundamental and most important aspects of JS programming.

JavaScript Visualized: Event Loop

WebAPI and setTimeout

What the heck is the event loop anyway? | Philip Roberts | JSConf EU

Let's Talk About Queues

Asynchrony: Under the Hood - Shelley Vohr - JSConf EU

JavaScript Visualized: Promises & Async/Await

Most of these videos/posts go into detail on the underlying mechanisms (such as the event loop) of asynchronicity and "concurrency" in JavaScript.

I hope that, after watching/reading these resources, you'll be able to grasp these concepts and get a deep understanding.


P.S. If you are hungry for more resources around async JS programming, you can take a look at the links I've gathered here




回答4:


When you say "create an object and set property x to value y", do you stop to think about how the JS runtime represents the object in memory? How about when you add a string "1" to the number 2? How does the runtime handle type coercion?

The understanding you have of setTimeout is all that's needed in order to use it. The same is true for function*, Promise, and async/await. How the JS runtime makes these possible doesn't really matter. You only need to hook up the pieces as intended.

That said, it will benefit you to understand them better over time. You can write better, faster programs by exploiting the strengths of the runtime, but this necessary to get started.



来源:https://stackoverflow.com/questions/63652929/javascript-asynchronicity-and-runtime

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!