-
-
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) |
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
I do agree with Ivaz, especially that
Yours
. Basically, you are doing synchronous coding with javascript (call and wait formykey1
to complete, then call and wait formykey2
to complete, then call and wait forJoe
, and then call wait forJulia
). You should indeed use some flow control library like mentioned Async, or Step, so that you callmykey1
,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