Instead of taking a callback, a function returns a promise, which can be daisy-chained to do work.
Ex:
file.read().then(console.log);
Or using the example in the article:
var a = fileA.read();
var b = fileB.read();
promise.all([a,b]).then((data) => console.log(data[0], data[1]));
Also Sync versions of calls in NodeJS are likely going to be deprecated, thus this won't even be possible in NodeJS going forward.
OS-level multitasking won't be able to achieve the same level of concurrency, but the simplicity and maintainability of the application will go up. The right choice depends on the needs of the application, of course.
Both evented and non-evented approaches have their place, and most server-side languages allow development with either approach: Ruby, Python, C, Java all have solid options for evented and non-evented solutions.
Do you have a source for this claim? I've seen it repeated many times, especially in the node.js community but I've yet to see any evidence to back it up. From what I've read, a synchronous threaded model can be just as fast as an event-based system [1].
This is touched upon in the slides you linked to. On slide 62 (SMTP server) a point says, "Server spends a lot of time waiting for the next command (like many milliseconds)." A malicious client could send bytes very slowly, using up a thread for a much longer period of time. If the client has an async architecture, it can open multiple slow connections with little overhead. The asymmetry in resource usage can be quite staggering.
I'd argue the root cause is... callbacks.
Asynchronous programming can be done elegantly, in a synchronous style using "async/await", originally (?) in C# [1], likely to be added to the next version of JavaScript [2], also in Dart [2], Hack [4], and Python 3.5 [5]. It can also be emulated in languages with coroutines/generators [6][7][8] (which in turn can be implemented by a fairly simple transpiler [9][10])
This:
function foo(a, callback) {
bar(a, function(err, b) {
if (err) {
callback(err)
} else {
baz(b, function(err, c) {
if (err) {
callback(err)
} else {
// some more stuff
callback(null, d)
}
})
}
})
}
Becomes this: async function foo() {
var a = await bar(a)
var c = await baz(b)
// some more stuff
return d;
}
And you'll see even greater improvements when using other constructs like try/catch, conditionals, loops, etc.[1] https://msdn.microsoft.com/en-us/library/hh191443.aspx
[2] http://jakearchibald.com/2014/es7-async-functions/
[3] https://www.dartlang.org/articles/await-async/
[4] http://docs.hhvm.com/manual/en/hack.async.asyncawait.php
[5] https://lwn.net/Articles/643786/
[6] https://github.com/petkaantonov/bluebird/blob/master/API.md#...
[7] https://github.com/kriskowal/q/tree/v1/examples/async-genera...
Not to mention, that the article's example is only comparing a small piece of work... A single node instance can easily manage 10K simultaneous requests... launching a thread per request, and you're going to hit resource bottlenecks at the CPU pretty quickly, compared to several node instances approaching a million simultaneous connections on a single server. Node uses not only an even loop, but a shared thread pool for isolation of work without blowing out resource contention.
I've seen issues with even a few thousand threads in poorly written simulation servers... going to an event-loop against a threadpool always worked out better under true load.
In the meantime, for those of us stuck with contemporary JS, there are mitigating code styles, in particular "early return":
function foo(a, callback) {
bar(a, function(err, b) {
if (err) return callback(err)
baz(b, function(err, c) {
if (err) return callback(err)
// some more stuff
callback(null, d)
})
})
}
Just to be clear, this is still inferior to a linear style.It is however much clearer than the "traditional" style.
They don't handle streaming data, but they're not supposed to; they're supposed to model the traditional JS function call model (single value return or single value exception) which they do well. For streaming data use some other paradigm (perhaps Observables from https://github.com/jhusain/asyncgenerator), but that's orthogonal to Promises, not a replacement for them.
The only other reasonable complaint I've heard is that they aren't cancellable, which is legit. There are proposals for that, though (https://github.com/petkaantonov/bluebird/blob/master/API.md#...).
At least with Promises functions actually return, as opposed to the continuation-passing-style of callbacks. (CPS is a fine intermediate representation for compilers, but it really shouldn't be written by hand; that's why we have compilers!)
- It implies that async execution is equivalent to callback hell. In reality there are excellent ways to have async code which looks just like sync (generators, async/await).
- It benchmarks multi-core (sync) vs single-core (async) and makes claims based on the results.
- It presents async execution as an antipode of clustering. In reality it's a best practice to make use of both.
...and everything that follows is just irrelevant.
> async cons: Adds latency with parallel overhead
What parallel? This should be cons for multi threaded running on single core cpu
> async pros: Callbacks help enforce error-prone synchronization
How does callbacks help in error prone synchronization? It is not callback, it is single threaded model that does.
I guess the author had to add the sort/reduce to "prove" his point...
If this had compared Node.js with clustering and async vs a synchronous language like Ruby, it might have been interesting. Maybe. But non-asynchronous operations in Node are an antipattern that core node contribs are trying to remove (the -Sync in node stdlib are trying to be removed).
Good coding conventions, promises, generators, and async/await are your friends for making callback hell go away.
Yes, if I'm writing a web server I want everything to be async. But, just as I want to share JS on the server and the client I also want to use JS as my build language. At least for me, I find it much faster to build using a sync style.
Maybe I just haven't learned the async way but for example I tried to make a build system using node. I needed to spawn out to a builder to build some stuff, copy files, spawn git in various ways to see if repos are dirty or clean, git add, git commit, and a few other things. I found it a massive nightmare and after 2 days I switched to synchronous python for my building. Was done in 2 hours.
If there's some articles or tips that will make me as comfortable at async for building as I'm as sync in python then please point me at them. But, for whatever reason, I'm struggling with async for really complex tasks. (and yes, I'm using promises)
And Node has the equivalent of WSGI and Rack: Connect/Express middleware.
Request per-process/thread has all the same memory overhead implication it has always had. It's almost like the author is ignorant of the reason for node.js' success or why it was built in the first place.
Also, "callback hell" is just FUD. Nobody who does this for a living and knows what they're doing really has an issue with this. Promises solve the unreliability issues, and async/await solves the syntax complexity issues.
I'd like to see this same analysis for 1000 req concurrency measuring memory overhead and using async/await for code comparison. Cooperative multitasking will always be capable of lower latency when you know what you're doing, and async programming is lightyears simpler than multi-threaded programming.
Launching that many threads will quickly hit bottlenecks on most systems... I've seen this happen in a poorly written simulation server (each actor had it's own work thread)... the server would freeze up randomly with only a handful of connections in a test scenario... changing to an event-loop using an async thread-pool resolved most of these issues (this was before node).
At Nginx conf the Nginx developers where showing interest to bring Javascript to the platform. They said that they will take similar approach that OpenResty uses (aka no callback hell).
Code looks like this:
var sync = require('./sync');
sync(function () {
try {
var result = someAsyncFunc1.callSync(this, 'param1', param2');
if (result.something === true) {
var result2 = someAsyncFunc2.callSync(this, 'param1');
} else {
var result2 = someAsyncFunc3.callSync(this, 'param1');
}
console.log(result2.message);
} catch (ex) {
// One of them returned an err param in their callback
}
});
I haven't tested the performance, so no idea if it's a running like a dog.https://www.discovermeteor.com/blog/wrapping-npm-packages/
It feels a bit like "get that sync code off my Javascript lawn!" but once you get used to it, it's pretty great in practice, and good for introducing newcomers.
The only real competition is transactional memory but it hasn't become mainstream yet.
I will say this, though, regarding the need for shared, mutable state: You can communicate by sharing memory, but you can also share memory by communicating. This is I guess what you allude to with database-only state, but it doesn't need to be a database. It could just as easily be a memcached server or some other fase key-value store.
We've been running a Koa.js API server using Cluster in production for over a year now with no hiccups (on a Windows machine).
I've been thinking about making the switch to iisnode, as it handles clustering, graceful shutdown and zero-downtime from within IIS (and does a couple of other things). It uses named pipes to proxy connections and also supports web sockets among other things.
With the nodeProcessCommandLine configuration setting, you can pass parameters to node (e.g. --harmony), use babel-node or io.js.
See: http://www.hanselman.com/blog/InstallingAndRunningNodejsAppl...
A blog post I wrote a while ago: https://shelakel.co.za/hosting-almost-any-application-on-iis...
The more annoying con is lack of shared memory. A single process can be much less complex when it doesn't have to worry about messaging systems and off process caching.
I understand that because if the funny scoping rules it means that threading is actually surprisingly hard? surely you'd want more control over your threaded event loops?