-
-
Save cgbystrom/724208 to your computer and use it in GitHub Desktop.
/* | |
node.js vs Python with Greenlets (say gevent) | |
I don't get why node.js is being so hyped. | |
Sure, the idea of writing things in JavaScript both on the client and server-side is really nice. | |
And JavaScript really fit an event-driven environment with its browser heritage. | |
But why on earth is boomerang code so appealing to write? I don't get. Am I missing something obvious? | |
All examples of node.js I see are littered with callbacks yet Ryan Dahl thinks coroutines suck. It doesn't add up for me. | |
Would anyone mind explaining how node.js below is superior to the Python code at the bottom? | |
Semi-pseudo node.js code with express.js follows: | |
*/ | |
var memcached = ...; // From node-memcached | |
var mongo = ...; // From mongoose | |
app.get('/', function(req, res) { | |
var a = null, b = null, c = null, d = null; | |
memcached.get('mykey1', function(err, result) { | |
if (err) { | |
sys.puts('Naive error'); | |
return; | |
} | |
a = result; | |
memcached.get('mykey2', function(err, result) { | |
if (err) { | |
sys.puts('Naive error'); | |
return; | |
} | |
b = result; | |
mongo.find({name: 'Joe'}).each(function(doc) { | |
// Errors handled by mongoose | |
c = doc.age; | |
mongo.find({name: 'Julia'}).each(function(doc) { | |
d = doc.lastname; | |
res.send('Hello World! ' + a + b + c + d); | |
}); | |
}); | |
}); | |
}); | |
}); | |
######################################################################## | |
# Compared to a Python semi-pseudo example based on Flask and gevent. | |
@app.route("/") | |
def hello(): | |
a = memcached.get('mykey1') | |
b = memcached.get('mykey2') | |
c = mongo.find(name='Joe').first().age | |
d = mongo.find(name='Julia').first().lastname | |
return "Hello World! %s%s%s%s" % (a, b, c, d) |
Well, synchronous code is definitely better. Easier to reason about. But I do see the advantages offered by node.js. Perhaps not so much from the callback structure, but for the similarity to client-side development.
You can start in node.js four processes at the same time: 2x memcached.get(...) and 2x mongo.find(,,,). Ater that use a common callback (or deferred). It will be asynchronous an usually faster.
I'm sure node.js with V8 is a lot faster than Python. What I was arguing about was that the code gets very hard to follow with complex callback hierarchies. With coroutines, found in gevent and Go for example, structure appears blocking and thus much easier to follow.
I wrote a blog post earlier this month of what makes node.js popular. http://blog.cgbystrom.com/post/9518338400/six-reasons-why-node-js-is-so-popular
node.js is an interesting plattform, but not so mature as Python world. Speed of the interpreter is not a first criterion, but the person who writes a code. I check node.js for some tasks, because I use JavaScript and Python in my projects.
Your Javascript it's just bad. You can write elegant code in Javascript, just check this library https://github.com/caolan/async. You can parallelize tasks elegantly with async, you don't need to chain callbacks...
I do agree with Ivaz, especially that Yours
. Basically, you are doing synchronous coding with javascript (call and wait for mykey1
to complete, then call and wait for mykey2
to complete, then call and wait for Joe
, and then call wait for Julia
). You should indeed use some flow control library like mentioned Async, or Step, so that you call mykey1
, mykey2
, Joe
,Julia
async'ly, and wait until all 4 complete and continue your code. At least, Memcached and MongoDB are called in parallel. That's the core philosophy of Node.js async I/O
requestHandler = function(req, res){
Step(
function(){
memcached.get('mykey1', this.paralell());
memcached.get('mykey2', this.paralell());
mongo.findOnce({name: 'Joe'}, {age: 1}, this.paralell());
mongo.findOnce({name: 'Julia'}, {lastname: 1}, this.paralell());
},
function(err, a, b, joe, julia){
if (err) {
sys.puts('Naive error');
return;
}
var c, d;
c = joe.age;
d = julia.lastname;
res.send(['Hello World! ', a, b, c, d].join(''));
}
);
};
app.get('/', requestHandler);
Do not hesitate to ask for code comment or correct me I am wrong.
app.get('/', function(req, res){
Step(
function(){
memcached.get('mykey1', this.paralell());
memcached.get('mykey2', this.paralell());
mongo.findOne({name:'Joe'}, this.paralell());
mongo.findOne({name:'Julia'}, this.paralell());
},
function(err, a, b, c, d){ res.send(['Hello World! ', a, b, c.age, d.lastname].join('')); }
);
});
should work too...
Both python gevents and nodejs use event loops, so technically same. But Python is much mature and broader platform. I agree with cgbystrom. From the js code snippets above it is evident that there are multiple ways to write the code (and functional style can get hard to read at times). That's where Python's simplicity and elegance shines (only one obvious way to do something!)
In terms of performance, nodejs wins.
With promises and ES2015:
app.get('/', function(req, res) {
Promises.join(
memcached.get('mykey1'),
memcached.get('mykey2'),
mongo.find({name: 'Joe'}),
mongo.find({name: 'Julia'}),
(a, b, joedoc, juliadoc) => res.send(`Hello World! ${a} ${b} ${joedoc.age} ${juliadoc.lastname}`));
});
I would agree that ergonomically speaking the Python code is much more desirable than the overly verbose way you wrote the Node Js/Express example. But even that poorly written Node code would run circles around the python code in terms of performance. Especially under a load.
@stevage 's example is more in line with how it should be written. Any newer version of node would support that right out of the box.
If you wanted to compare an equivalent python attempt at this you would need to use asyncio and at that point, you can easily get into callback hell. (Although it can be written nicely as well)
It seems to me like your gripe with node is developer ergonomics. Your apparent lack of or disregard of domain knowledge makes a comparison very one sided. Furthermore, it's not always about how you feel. Sometimes you need to take performance into account as well. (Not saying python is sluggish by any means) I'm not saying that async programming is a silver bullet, but Node JS was built from the ground up to be async, and its adoption is a result of many things but IMHO the important thing here is IO.
To answer the question posed in your gist, the appeal to me is the performance of async IO, less context switching when doing full stack, robust error handling in callbacks, and it can handle a tremendous amount of requests with fairly little resources. This makes it cheaper to scale, and the end user experience prevails because of it.
My world is divided in two, I love Node.js speed and Python simplicity.
Super late to the discussion, but the original Python code is synchronous, right? If not, gevent
or whatever is magic and magic is never easy to understand.
@ephetic the original python code is not synchonous. It's using gevent (coroutine-based async library) which does monkey-patching of blocking python functions and replaces them with async counterparts. As a result code looks like synchronous but is run in a same way as Node.js does. And for me looks much more elegant than JS one (even in the last sample by @stevage).
May wil be interesting for someone who want make python async more fast - https://github.com/MagicStack/uvloop
Probably the fact that you can error-check on the callbacks and act accordingly. I sort of agree with you, though. Python code is more elegant, but JavaScript, to me, is easier to write. For some reason, JS "clicks" better in my head.