JavaScript Concurrency Model and Event Loop
developer.mozilla.org
developer.mozilla.org
https://jakearchibald.com/2015/tasks-microtasks-queues-and-s...
http://www.c-sharpcorner.com/article/overview-of-micro-tasks...
Firefox 53 produced a log that matched what I expected but then I ran it a few more times and then it happened in another order which I then read was the order they were supposed to appear.
>Microsoft Edge, Firefox 40, iOS Safari and desktop Safari 8.0.8 log setTimeout before promise1 and promise2 - although it appears to be a race condition. This is really weird, as Firefox 39 and Safari 8.0.7 get it consistently right.
So Firefox 39 may have gotten it consistently right on his machine, Firefox 40 did not on his machine and Firefox 53 still does not on my machine.
That seems like a dangerous, misleading statement.
setTimeout(() => {
var x;
while (x < 2000000) {
x += 1;
}
});will block. Event listeners, timeouts, and xhr requests just wait to receive a response. They're not actually doing anything while that happens. And their callbacks basically just become 'synchronous' when they get that response.
The analogy I had heard was that of a check-in window at a doctors office: the nurse would give you a form to fill out and when you were done, you could get back in line and hand the form back to the receptionist. I think the analogy was trying to show that there weren't multiple windows to check in at, nor did you have to stand at the window while you're filling out the form, preventing people behind you from getting the form. But the reason it's a bad analogy, and why I was confused, was that it made it appear that you (the patient) are the callback function, and you're doing some processor intensive work when you sit down to fill out the form. It would have been clearer had the analogy specified that you are actually a backend server/web-worker, but even that doesn't fit right.
So, I think it would be best to avoid using analogies. The event loop isn't that complicated: async functions just sit around waiting for a response, and they stick their callback on the event queue when they get that response. When the call the stack is cleared (from it's synchronous functions), the first callback in the event queue moves to the call stack (ie, it gets called), and that process repeats until the queue is cleared.
If I try to forget all I know about JS, I can think of two different _wrong_ ways to interpret this code:
a) executes the callback immediately in-line, as it was synchronous
b) executes the callback at a later time, in a different thread.
Yup, I know, that's why I gave an explanation :)
> And you very well could move that code to a different thread via a web worker, couldn't you?
localStorage is not thread safe, so no probably not.
But since the language is single-threaded (or at least, it defines nothing about multiple threads with shared memory), there is a strong incentive not to have blocking functions.
The event loop architecture is also heavily used in iOS / Cocoa, although it is often not well understood by developper. Each thread has an event loop, including the main UI thread, and many weird behaviors can be understood better once you know a bit about them.
Which made me wonder if a simple implementation of agent based concurrency in swift server couldn't simply be one agent - one event loop, plus a way to prevent direct calls across agent boundaries. Server is not iOS, but i suppose some language facilities should already be there and make it easier to implement.
/side topic
But it is interesting that it doesn't mention the oft repeated meme "JavaScript is single-threaded". Would be nice with an example showing parallel number-crunching without WebWorkers. Is that possible?
Lately, I've been playing with libev and thread-pools together in Nim; so you write async/await stuff everywhere with futures, including number crunching, which gets delegated out to the thread pool and yields back to your main thread when it's done. It's quite nice, but I forgot how complex this stuff gets!
Not quite, since using threads internally (thread per IO operation) would defeat a purpose of async system, which is created specifically to not pay threading cost (context switches, etc.). In layman terms, kernel will simply invoke your C callback function when "stuff happens", and libuv propagates that to JS in platform-agnostic manner.
There are still threads and thread pools in V8 and node for stuff like calculating PI (or, more realistically, password hashing and other number crunching), but that is mostly unrelated to async IO itself.
Some google queries: select, poll, epoll, kqueue…
Yes and no.
Having single threaded JS (no matter if runtime uses epoll) allows for JS concurrency concerns to be limited to a small number of places, i.e no pre-emptive. This makes concurrency in JS easier to reason about.
You can fake it by kicking off multiple executions on a data set and chunk the work load across the data set, so they progress incrementally and equivalently. The data might be small enough (or browser fast enough) to give the impression of things being done in parallel.
It's a somewhat "new" feature of JavaScript but most modern browsers already implement it[2].
[1] https://developer.mozilla.org/en-US/docs/Web/API/Web_Workers...
OP asked for an example sans web workers.
Plus web workers do not change the fact that JS is single threaded.
Generators and yield for some types of crunching. Not parallel, but at least not a tight spinning loop.
Single-threaded is desired behavior in the case of web ui and of Node, which is multi-process c/++ under the hood and JS responding to message queues.
It's pretty simple. Functions block. The messaging queue that triggers function calls in event-driven, setTimeout, XHR, talking to another JS runtime cases doesn't. So yes, you have to be careful to not lock up your entire server by processing images with JS function calls in the same runtime.
But the huge benefit you get out of this tradeoff is that you don't ever have to worry about two things trying to change something at the same time. Everybody is locked until your functions are finished and with async that doesn't typically take very long.
The synchronous lib calls in Node are mostly for convenience. I use them all the time when doing simple file I/O stuff where I just want to read something from a file, do something to it and stick it in a var or a new file. In a server or more complex application, however, you always want to use async callbacks.
What I would like to find is _why_ do they work like this. What is so important that this is the way it must be done. We live with browsers that lock up for a while until they figure out that some code has inadvertantly done a while(true). Promises and similar callback amelioration techniques took ages to turn up, when without the event model they might never have been required at all. Is there a reason for this? Is it a good reason?
The history of JavaScript is that it started as a sort of "quick and dirty" scripting language at Netscape. Creating a completely separate event model wasn't possible in those circumstances, so JavaScript ended up being shaped by the single-threaded lowest common denominator of Win32 and other platforms that Netscape ran on.
You also don't gain a lot from multithreading in typical UI event handling. Most event handlers have to finish quickly anyway or the user will notice lag in updating the display. A UI that responds to events slowly and out of order would confuse users, even if it had no concurrency bugs (which is unlikely).
As the UI uses the same thread as JS, if JS blocks, this would lock up the whole window (clicking on things would do nothing), so it's important functions don't take too long or suspend the thread.
There was an experiment within servo to see if rendering and JS could run in separate threads entirely, but I'm not sure how that went? https://github.com/servo/servo/wiki/Design
To gracefully establish expectation in the user - A processing indicator will buy the application some time before the user gets impatient. The setTimeout 0 pattern is useful to delay the long running process enough for the browser to redraw the UI to throw up the processing indicator.
One can further achieve another state in the processing indicator by leveraging time delayed CSS animation which runs in a thread that's parallel to JS. So you could use CSS to augment the content of the processing dialog while the blocking JS is running.
If the article had used I/O (http request) as an example of event handling (message queuing), it would have added some concurrency context.