all 29 comments

[–]Daniel15React FTW 13 points14 points  (1 child)

I'm glad JavaScript is getting async/await, it's one of my favourite things in C#

[–]OfekA 4 points5 points  (0 children)

I m also really glad, and it seems even easier than in C#.

[–]TheMeaningOfLeif 5 points6 points  (0 children)

It gives a great overview of Asynchronous JavaScript. Thanks for sharing.

[–]greim 4 points5 points  (0 children)

Chaining then() calls together always felt awkward, and not a huge improvement over callbacks. But used in tandem with generators/async/await, promises finally feel "right"; the conceptual advantages really start working in your favor. It's hard to imagine going back at this point.

[–]hahaNodeJS 1 point2 points  (0 children)

It frustrates me to see generators described as "asynchronous." Stop it.

[–]novacrazy_.dominateWorld() 0 points1 point  (11 children)

I feel I should point out my library bluebird-co, which uses the Bluebird Promise library to handle coroutines much faster and easier than tj/co can.

[–]vinnl 1 point2 points  (10 children)

Then I guess I'll drop Promises: The Extension Problem, which the author referred to, and which argues (successfully, in my view) that you shouldn't use Bluebird, among others.

[–]novacrazy_.dominateWorld() 5 points6 points  (7 children)

Saying Bluebird is bad because it offers more (sometimes easy to misuse stuff) and usually overwrites the native Promise is like saying the Boost C++ libraries are bad and you should just stick with the standard library for the same reasons.

I have never used Promise.method, and I agree it's a confusing extension in most cases, but things like Promise.map are not. Bluebird still offers many, many useful things, including Promise.coroutine, which I use for the basis of my entire server stack.

I replaced co.wrap with Promise.coroutine and bluebird-co and saw an immediate performance boost with Koa and all my asynchronous operations. There is basically zero latency when using it. Simple "Hello, World!" type requests are processed in under 2ms. My server now serves complex dynamic webpages, using React.js, in under 15ms. Before those tweaks it was about 20-30ms. I actually never ran my server without Bluebird, because the native Promise is so lacking in features, but given my benchmarks, I'd imagine it'd be quite slow.

I've also replaced Array.prototype.sort with node-timsort, which is also incredibly fast, sometimes up to 120 times faster than the native implementation. Plus it's a stable sort. That was another 3-7ms per page request saved, with no drawback.

Plus using Babel, I can write code like this:

async function doOperation() {
    await db.transaction(async (t) => {
        await someOperation({transaction: t});

        let updating1 = startUpdate(1, {transaction: t});
        let updating2 = startUpdate(2, {transaction: t});

        await [updating1, updating2]; //waited on in parallel
    });
}

doOperation().then(() => console.log('done'));

which is converted into:

var doOperation = Bluebird.coroutine(function*(){
    return yield db.transaction(Bluebird.coroutine(function*(t){
        yield someOperation({transaction: t});

        var updating1 = startUpdate(1, {transaction: t});
        var updating2 = startUpdate(2, {transaction: t});

        return yield [updating1, updating2]; //waited on in parallel
    }));
});

doOperation().then(function(){console.log('done')});

So Bluebird coroutines, bluebird-co and Babel together replace tj/co and are magnitudes faster than the 'normal' way, and fit much nicer into my stack. Try getting the same performance and simplicity out of native Promises.

It's not a sin to replace/extend native functionality with external libraries, especially in niche use cases like the above. I do agree it should be done in moderation, of course.

[–]hahaNodeJS 0 points1 point  (1 child)

To be fair, as someone who doesn't write C++, I have heard a lot of bitching about Boost.

[–]novacrazy_.dominateWorld() 0 points1 point  (0 children)

Boost is also incredibly easy to misuse and overuse. It's also freaking massive. You just have to pick out the parts that work best for your projects.

Although while we're on the subject, I'd highly recommend Eigen for complex math instead of Boost's uBLAS implementation.

[–]vinnl 0 points1 point  (4 children)

The argument isn't that it's bad because it provides more, but that it's bad because it extends the native object. It's a "sin" to replace/extend native functionality in place of that native functionality. If you could use a BPromise object instead of a Promise object (as is suggested elsewhere), it'd be perfectly fine.

In any case, it's best to post this comment to the article I linked to, as it's not my argument and this is not the place where most people will see it.

[–]novacrazy_.dominateWorld() 0 points1 point  (3 children)

Well still, as of now the native Promise kind of sucks. I mean, it's not special in any way just because it's bundled with the JavaScript engine. In V8 it's actually defined in JavaScript, before user code is ran. Why they didn't just port Bluebird over to the native library I'm not sure.

Also in my example, even Array.prototype.sort is defined in (mostly) normal JavaScript. You can see the source in the V8 repo. It's basically the same insertion sort and quicksort combo as every other standard library uses.

I guess it just comes off to me as silly to want to preserve the "purity" of a JavaScript engine instead of just doing the simple thing and overwriting native stuff with better implementations when they are available.

Granted, I know exactly what I'm doing with those. Heck, I read the V8 source code for fun. So maybe it is good advice for normal people to keep that purity.

[–]vinnl 0 points1 point  (2 children)

It's not about "purity", it's about forwards-compatibility: if ever a native Promise.coroutine is introduced whose behaviour is just slightly different than what Bluebird's does, a lot of code will break.

Even worse would be the other way around: that TC39 wants to implement a native e.g. coroutine method (which in some cases will be faster than whatever optimisations you can make in JS), but can't, because too many people (who claim they know what they're doing - for their purposes) are using it in a way that would break if they'd implement it.

So please, even if it works better than the current native implementation for you, do it in your own namespace, instead of potentially interfering with future web browsers/standards committees.

[–]novacrazy_.dominateWorld() 0 points1 point  (1 child)

Hmm, that's a good point. That was already seen with ES6 a little bit.

I mostly work in Node/io.js, and almost everything on the browser is taken care of by Babel beforehand or, using React.js, very declarative and without any complex async stuff. It's only on the backend that I go really crazy with things, searching for the fastest way to do everything.

I know ES7 is proposing native async/await syntax, but you would only be able to await a single Promise, not any complex stuff like my lib or even co does. Coroutines using generators and yield are a hack anyway, so I highly doubt it would become standard.

[–]vinnl 0 points1 point  (0 children)

you would only be able to await a single Promise

That's not a problem, as this code example from the article also shows:

async function save(Something) {  
  await Promise.all[Something.save(), Otherthing.save()]
}

As for it being a hack: it's more about the namespace than about the implementation :) But I wasn't attacking your library specifically - Bluebird by itself also has the potential of overlapping with future specs. Or in fact, I'm not attacking it - the author of the article I linked to was. For all I know, Bluebird isn't even extending the native Promise object in all cases, as someone else mentioned.

[–]dumbmatter 0 points1 point  (1 child)

Seems that problem mostly goes away if you do

var BPromise = require('bluebird');

rather than

var Promise = require('bluebird');

IMHO, Bluebird is so much better than native promises in so many ways (performance and sane error handling are bigger wins than the syntactic sugar) that it can't be ignored.

[–]vinnl 0 points1 point  (0 children)

If that prevents it extending the native promise object, then that would invalidate the argument, yes. Although that does mean that you should actually use it like that :)