8 ms·
> JavaScript just happened to be a nice fit to evented, non-blocking I/O, and a single-threaded event-loop based environment. I keep seeing variations on this
by pdpi 12y ago
> JavaScript just happened to be a nice fit to evented, non-blocking I/O, and a single-threaded event-loop based environment.
I keep seeing variations on this statement, and I have never figured it out. What, exactly, is it about JavaScript that makes it particularly appropriate for this paradigm?
The way I see it, the only abstraction JS provides that helps with this is first class functions, which are also present (and often richer and more robust) in many other languages.
- panic 12y agoJS isn't even particularly good at evented, non-blocking I/O: chains of asynchronous calls become series of ever-more-indented function(){}s. Languages with coroutines (Lua) or first-class continuations (Scheme) can chain asynchronous calls in a more straightforward style.
- taurath 12y agoThere are dozens of very decent ways to handle flow control with JS - any time you see indented function chains it tells you this might be poor quality code. For better or worse you can choose a library or write your own - Async.JS, Promises, etc all deal with this. Some abstractions like Async.Auto handle branching paths (series -> parallel -> series) very elegantly.
- meric 12y agoWith coroutines in Lua, you could write: function handleRequest(req) data = db.fetch("user") return render("template.html", {user=data}) And if db.fetch and render (both made up functions) are written using the coroutine paradigm, your code would automatically be evented and non-blocking, no need to use libraries to handle your callbacks, and your code has the same style it does if it were blocking. In db.fetch it uses the "yield" keyword that would tell Lua it is about to do an async operation it should wait till it finish before continue running Lua code, meanwhile run something else instead, making it non-blocking and you can run many of these Lua programs at once on a single thread, all of them concurrently.
- ufo 12y agoOne nice thing about Lua coroutines is that they are "stackful". You don't need to chain "yields" all the way up the call stack (like Python's "yield from") so its possible to create functions that work with both sync and async callbacks. function mapList(xs, f) local ys = {} for i=1,#xs do ys[i] = f(xs[i]) end return ys end mapList({10, 20, 30}, function(x) return db.fetch(x) end) In JS you can't use async functions inside of Array.prototype.map. You need to use a separate async-aware method from your favorite async library instead.
- raksoras 12y agoI have used this idiom to great results in my project (https://github.com/raksoras/luaw https://github.com/raksoras/luaw). Basically, request:read() hooks up into libuv (node.js' excellent async IO library) event loop and then yields. Server is now free to run next coroutine. When socket underlying the first request is ready with data to read libuv event loop's callback gets fired and resumes original coroutine.
- meric 12y agoThat's awesome. You've me hooked. For postgres database access, what library do you suggest? Also, I remember lua-nginx can also do non-blocking IO in blocking style code. (http://wiki.nginx.org/HttpLuaModule http://wiki.nginx.org/HttpLuaModule, http://openresty.org http://openresty.org) How does luaw compared to that?
- raksoras 12y agoRight now it's just a HTTP server and REST framework. It's a very first release and I don't have any DB drivers for it yet- they would need to be non-blocking as you mentioned. I have plans to write dbslayer like "access DB over REST" service as a companion to Luaw so that it can use any and all databases that have JDBC drivers available without having to write non-blocking driver specially for each new database. This kind of arrangement where DB connection pooling is abstracted out of application server itself has other advantages related to auto-scaling in cloud and red/black or "flip" code pushes at the cost of slightly more complex deployment. All depends on how much spare time I actually get :(
- TheCoelacanth 12y agoJS has dozens of decent ways of handling it; the other languages mentioned have one good way of handling it.
- vkjv 12y agoLots of indented function calls is just poor design. You get the same thing in Scala when you start dealing with lots of Futures inappropriately. Just choose a decent abstraction for callbacks. I prefer promises, but async.js does a good job as well. The `flatMap` function on a Future in scala might be built into the language, but it is implemented and scala and not significantly different (in my mind), to Bluebird being implemented in javascript.
- flaki 12y agoAccording to Brendan Eich (http://devchat.tv/js-jabber/124-jsj-the-origin-of-javascript-with-brendan-eich http://devchat.tv/js-jabber/124-jsj-the-origin-of-javascript...), early on JavaScript was inspired by Scheme and the initial plan was to create a language that was "Scheme in the browser". :)
- wtbob 12y agoI prefer Common Lisp, but I can only dream about how wonderful Scheme in the browser would have been. JavaScript is hideous, simply hideous: it's a hack, piled atop a thousand compromises, wrapped up in a million curly brackets. Every time I use JavaScript I imagine how good life could have been were it Scheme. Every time I have to use JSON I imagine how great life would have been were we using canonical S-expressions[1] instead. There's one good thing—and only one good thing—about JavaScript: it's incredibly well-deployed. As another commenter mentioned, JavaScript is an object lesson in path dependency. In many ways, it's appropriate that it has 'Java' in the name: it's popular, but it's ugly. There are better languages; indeed, nearly every other non-Turing-tarpit language is better than either Java or JavaScript: Lua, Lisp, TCL, Python, Rebol, Erlang. [1] http://people.csail.mit.edu/rivest/Sexp.txt http://people.csail.mit.edu/rivest/Sexp.txt
- nissimk 12y agoIt all boils down to: Javascript developers were already used to this paradigm in the browser.
- vkjv 12y agoThis. A thousand times, this. It's a really good model for something that is I/O bound because it is easy to reason about. This makes the model perfect for events in the browser (both DOM events and XHR). It's not that Javascript is especially suited to this model, but thanks to the browser, it was already the most used implementation of this model.
- tracker1 12y agoI think this combined with the fact that JS engines were already relatively isolated from the browser itself and able to be embedded with other software (in the case of node.js libuv/libev) made it a really good option. require+npm added a lot more to the mix. But it was the broad availability of mindshare of those developers who at least knew some JS that really kicked it over the top, given JS as a DSL for I/O bound applications.
- falcolas 12y agoPerhaps they're used to it, but are they really fluent in it? Because I keep seeing otherwise bright developers having to use abstraction upon abstraction to keep from screwing up event driven callbacks. Underscore, Flux, promises.js, Async, Angular, Node fibers, Step... How much is all this abstraction costing us, both in terms of computation time and developer time (both writing and debugging)?
- afjgasdfjlasdf 12y agomaintenance time, too. We're in something of a JS framework boom (or hell, if you wish) right now. These people are developing as if their shit libraries won't exist in 3 years. Hence, the total lack of documentation and ongoing maintenance from so many of them. I've already had to maintain code that used abandoned JS frameworks. I'm a little scared of what's around the corner here...
- chubot 12y agoNot just first class functions -- anonymous functions, and closures. Python has first class functions, but not anonymous functions, since lambdas aren't full functions. And Python doesn't have closures, because you have to use "global" or "nonlocal" (in Python 3) to assign a a variable in an enclosing scope. These features let you write concurrent state machines in JS without explicit state -- you basically use the stack as state. Python was pretty early in terms of event loop programming for interpreted languages -- asyncore in the stdlib, and Twisted. But I agree that the node.js style is nicer than asyncore, Twisted, or Tornado. I prefer coroutines over state machines, but if you're going to write state machines, than doing it with anonymous closures is nicer than doing it "manually" with classes and so forth. Tcl was even earlier, but Tcl is pretty foreign to most people, and was more of an embedded language than a scripting language. Pretty sure Tcl has full anonymous closures, but it is a bizarre language in some other ways. I haven't seen the event loop style in Perl but I think you can do it. If someone has an example I'd like to see it. To me, Perl only barely qualified as a real programming language because you can't even name your function params? At least when I used it, which was over 10 years ago.
- pdpi 12y agoOn the other end of the spectrum, Ruby blocks (and all the attached machinery) feel to me like a much nicer abstraction to work with. Go's goroutines and channels show giving a language purpose-built concurrency primitives is a really nice model (I expect Erlang would be another good example of the same, but never tried it), and the monadic approach in Haskell makes it so that you could almost directly translate node.js code and end up with something far more readable.
- ufo 12y agoTBH Python is a bit of an exception there. Most languages with first-class functions (specially more modern ones) will also come with anonymous functions that aren't intentionally crippled (like Python's lambda is) and will have more traditional lexical scoping instead of local-by-default.
- chubot 12y ago
- Mikeb85 12y ago> What, exactly, is it about JavaScript that makes it particularly appropriate for this paradigm? V8. And it's a pretty powerful language. C-like syntax (familiar), Lisp-like (powerful/flexible), it's everywhere and it's fast (thanks to V8). Really, if it weren't for Chrome, Node.js wouldn't be a thing, and JavaScript wouldn't be ruling the world.
- gcp 12y agoI think JavaScript and the associated performance war was already well underway before Chrome even existed.
- azakai 12y agoYes, JITs in WebKit and Gecko showed up around the same time as v8, or a little earlier even. And the WebKit JIT (Nitro) beat v8 on various benchmarks. It's still possible v8 somehow sparked those other JITs, if before it was public the developers of WebKit and Gecko learned of v8 and stepped up their game. But, I don't know if that's true or not. Anyhow, it's an impressive achievement that people still mention v8 as the reason JavaScript is fast, when it isn't the fastest today, and wasn't the first to be fast historically. Although, it is certainly a worthy VM, just one among several.
- pdpi 12y agoRight, but V8 doesn't make the language itself particularly appropriate. (And calling it "lisp-like" is way over-reaching. It has first class, anonymous functions and closures, and that's it.) To me, the node story seems to boil down to "V8 was a readily-available fast runtime and people who already knew JS flocked to it" — which is fair enough, but hardly an argument for how appropriate JS is, as a language, for this sort of programming model.
- nostrademons 12y ago"V8 was a readily-available fast runtime and people who already knew JS flocked to it" That's basically all there is to a language's success. We like to debate syntax and closures and continuations and type systems and immutability and concurrency models on programming language message boards, but realistically, nobody cares. The two questions they have when they encounter a new language are "Can I learn this in a weekend?" and "Can I build cool things that other people would actually want to use?" If you look at the history of programming technologies that have "won", the list includes C++, Java, Javascript, PHP, C, Objective-C, and to some extent Perl, Python and Ruby. The first 4 of those are terrible from a language-design standpoint, but the two things they all had in common were a readily-available reasonably-fast runtime (except PHP, and that was "fast enough" for the things people use it for), and a familiar syntax. C, PHP, Javascript, and Objective-C also had the benefit of being the "native" language for a major application platform, which seems to be the other major critical success factor for a new language.
- al2o3cr 12y ago"What, exactly, is it about JavaScript that makes it particularly appropriate for this paradigm?" Devs with experience writing interactions with the DOM had already convinced themselves that callbacks nested ten deep was a totes OK way to write code, "releasing Zalgo" and all.
- bcoates 12y agoAlmost every popular programing environment provides an incredibly unsound shared-memory multithreading library in the distribution, which winds up being heavily used by every other library--even sometimes built-in ones. Javascript in both the browser and not refrains from this, making it generally safe to use not-invented-here code. In other languages, you could use libuv or tornado or aio or whatever, but it's like throwing away the entire ecosystem and using an obscure language anyway.
- mkramlich 12y agoJS is in every web browser. thats why. number one. period. because there are plenty of other language syntaxes compatible with non-blocking mono-threaded EDA.
- surrealize 12y agoIn JS, you know that library code that you call almost certainly won't do blocking I/O. If you're trying to do non-blocking async stuff in other languages, you have to hope/check that none of your code and none of your dependencies try to do blocking I/O. (Unless you're using something that does async under the covers like haskell or go)