Skip to content

Instantly share code, notes, and snippets.

@cgbystrom
Created December 1, 2010 20:56
Show Gist options
  • Star 12 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save cgbystrom/724208 to your computer and use it in GitHub Desktop.
Save cgbystrom/724208 to your computer and use it in GitHub Desktop.
node.js vs Python with Greenlets
/*
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)
@Garciat
Copy link

Garciat commented Jan 28, 2011

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.

@cgbystrom
Copy link
Author

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.

@devhd
Copy link

devhd commented Jun 3, 2011

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.

@cgbystrom
Copy link
Author

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.

@cgbystrom
Copy link
Author

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

@devhd
Copy link

devhd commented Sep 15, 2011

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.

@lvaz
Copy link

lvaz commented May 4, 2012

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...

@truongsinh
Copy link

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

@truongsinh
Copy link

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.

@tobsn
Copy link

tobsn commented Jun 4, 2013

@truongsinh

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...

@vijayphadke
Copy link

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!)

@pyrofolium
Copy link

In terms of performance, nodejs wins.

@stevage
Copy link

stevage commented Dec 9, 2016

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}`));
});

@charliemitchell
Copy link

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.

@franklinrincon
Copy link

My world is divided in two, I love Node.js speed and Python simplicity.

@ephetic
Copy link

ephetic commented Apr 4, 2017

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.

@alexei-sch
Copy link

@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).

@worldmind
Copy link

May wil be interesting for someone who want make python async more fast - https://github.com/MagicStack/uvloop

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment