Bluebird – A full-featured, fast promises library for JavaScript
github.com
github.com
What's also really impressive is that ultra-fast means callback-level performance. And that performance is achieved even though promises are exception-safe (and don't crash the process like exceptions in callbacks ;)
In fact, this library may become the turning point that causes node.js userland to start switching to promises...
Would someone be kind enough to give me some idea what the meaningful differences are between Q, when, and bluebird?
Q has historically stemmed from the work of Mark Miller, one of the authors of the E programming language, from which JavaScript promises are largely inspired. As such, it aligns with his larger goals for a promise ecosystem, which include object-capability security and using promises as proxies for remote objects. For an example of promises applied in these contexts, see his paper at [1].
In practice, all of this is somewhat theoretical, as Q also serves a large constituency of "normal" promise users in Node and the browser. There, it differentiates itself via a focus on features and integrity over speed (mainly as a matter of prioritization; we like speed too!); excellent Node.js integration [2]; and an internal system which allows promises for remote objects via Q-Connection [3] (but also gives some nice methods for promises for objects etc. for the same-machine case [4]).
I would say that Q is my favorite library to work with, as it has thought out its design choices in a depth I don't see in the other libraries---or perhaps more accurately, the choices they have made don't align with my sense of craftsmanship as well :). That said, you should probably not use it if your concern is speed. We have always taken the position that, while speed is nice, most of the time you are doing an asynchronous operation anyway, so any overhead from the promise library will matter very little.
[1]: http://research.google.com/pubs/pub40673.html [2]: https://github.com/kriskowal/q/wiki/API-Reference#interfacin... [3]: https://github.com/kriskowal/q-connection [4]: https://github.com/kriskowal/q/wiki/API-Reference#promise-fo...
The background is interesting, but I'm not sure what value an association with historical context brings. I've tried other promise libraries, but stub my toe on missing capabilities I'm used to, or end up distracted with mechanics. I don't think I've ever found myself saying, gee, would that this be closer to ref_send, but then again almost all implementations happen to tack close to. So, uh, Domenic does have a more useful account of Q. Mostly here to drop some ref_send. ;)
I imagine you could re-caption the "Simon Peyton-Jones adding the IO Monad to Haskell" GIF [1] to serve this topic, too.
[1]: http://this-plt-life.tumblr.com/post/44462204757/simon-peyto...
results for 20000 parallel executions, 1 ms per I/O op
file time(ms) memory(MB)
promiseishBluebird.js 1076 76.08
async.js 2634 113.79
promiseishKew.js 2654 123.50
promiseishQ.js 56508 804.21
note: async.js is basically waterfall from https://github.com/caolan/async[1]: http://jsperf.com/promise-comparisons/55 [2]: http://calvinmetcalf.com/post/61761231881/javascript-schedul...
Also, in your benchmark you do output in bluebird section with console.log. Maybe try http://jsperf.com/promise-comparisons/50.
My background is that I've been trying to create a promise library that just creates without also being underscore for promises [1]
What sort of performance are we talking about that we are able to test without really measuring other things. For instance when was able to dominate many of the perfs by actually doing many of them synchronously in cases where other libraries would force it async.
[1]: http://jsperf.com/promise-comparisons/59 [2]: https://github.com/calvinmetcalf/lie
However, on node.js you can have more than 1 client served by your server. For instance, your server could have e.g. 10000 promises/callbacks/generators active at a time. Take a look at how different solutions handle that https://github.com/spion/async-compare/blob/master/latest-re....
The results don't change at all if we for example increase latency of I/O by an order of magnitude - that's why latency is not an interesting metric for this use case. It's pretty intuitive, all the 10000 users will just get their results 9ms later but the fact that we could even handle them in the first place without crashing our server matters.
----------
You are right that for your use-case, pretty much the only thing that matters is what implementation is used for queuing async calls since it's such a tight bottleneck. Bluebird attempts to use mutation observer if available.
Are you going to delay or force that?
https://gist.github.com/radiosilence/6826930
Could this be improved?
It uses Q, not bluebird, but should still be relevant.
Call me hater, but, I would never use it and anything depending on it.
What it does: not clear.
How it's different: not clear.
Project name: bird?!
Modularity: Poor. one single JavaScript file with hundreds of lines :(
Duplicate effort, nothing new but more confuse.
I'm just tired of seeing shitty Promises libraries. And I'm a huge hater for those.
Here is a good one that I liked: https://github.com/puffnfresh/fantasy-promises
1. Its a promise library. It gives you a Promise class, a PromiseResolver and some tools to convert node functions to promises or to support dual promise/callback API.
2. Its super-fast and has full stack traces that go back until the beginning of the async event chain
3. I will skip this, because, uhh...
4. You didn't look hard enough. https://github.com/petkaantonov/bluebird/tree/master/src
5. Yes, promises are confusing. I'm going to write a straight-forward introduction (that uses bluebird) this weekend. But once you get past the new words and definitions, promises are actually not complicated at all. Infact, here is a short intro:
How are promises different? Instead of writing your functions to take callbacks, you write functions that return promises. Imagine that node's fs.readFile returned a promise instead of taking a callback. Then this code:
fs.readFile(file, function(err, res) {
if (err) handleError();
doStuffWith(res);
});
would look like this: fs.readFile(file).then(function(res) {
doStuffWith(res);
}, function(err) {
handleError();
});
Pretty much the same so far, except you use a second callback for the error (which isn't really better). So when does it get better?Its better that you can attach a callback later if you want. Remember, `fs.readFile(file)` returns a promise now, so you can put that in a var:
var filePromise = fs.readFile(file);
// do more stuff...
filePromise.then(function(res) { ... });
Okay, that's still not much of an improvement. How about this then? You can attach more than one callback to a promise if you like: filePromise.then(function(res) { uploadData(url, res); });
filePromise.then(function(res) { saveLocal(url, res); });
Still not good enough? What if I told you... that if you return something from inside the .then() callback, then you'll get a promise for that thing?Say you want to get a line from a file:
var linePromise = fs.readFile(file).then(function(data) {
return data.toString.split('\n')[line];
});
var beginsWithHelloPromise = linePromise.then(function(line) {
return /^hello/.test(line);
});
Thats pretty cool. But the coolest thing is probably this: if you return a promise from within `.then`, you will get that promise outside of `.then`: function readUploadAndSave(file, url, otherPath) {
// read the file
var filePromise = fs.readFile(file);
// upload it when done reading
var uploaded = filePromise.then(function(content) {
return uploadData(url, content);
});
// also save to another place locally
var savedLocal = filePromise.then(function(res) {
return fs.saveFile(res, otherPath)
});
// return a promise that "succeeds" when both saving and uploading succeed:
return Bluebird.join(uploaded, savedLocal);
}
readUploadAndSave(file, url, otherPath).then(function() {
console.log("Success!");
}, function(err) {
// This function will catch *ALL* errors, from the above
// operations including any exceptions thrown inside .then
console.log("Oops, it failed.", err);
});
Now its easier to understand the chaining. At the end of every function passed to a `.then()`, return a promise. Lets make our code even shorter: function readUploadAndSave(file, url, otherPath) {
// read the file
return fs.readFile(file).then(
// upload and save when done reading
return Bluebird.join(uploadData(url, content),
fs.saveFile(res, otherPath));
});
}
What if we want to make sure the data is uploaded first before saving? function readUploadAndSave(file, url, otherPath) {
// read the file
var uploadedPromise = fs.readFile(file).then(
// return promise that its uploaded
return uploadData(url, content);
});
return uploadedPromise.then(function() {
// return promise that its saved.
return fs.saveFile(res, otherPath);
});
}
Or even shorter, skip the temporary promise variables and chain .then the way we chain `stream.pipe` in node: function readUploadAndSave(file, url, otherPath) {
// read the file
return fs.readFile(file).then(
// then upload it
return uploadData(url, content);
}).then(function() { // after its uploaded
// save it
return fs.saveFile(res, otherPath);
});
}
And similarly to how in a stream.pipe chain the last stream is returned, in promise pipes the last returned promise is returned.Thats all you need, really. The rest is just converting callback-taking functions to promise-returning functions and using the stuff above to do your control flow.
Ah, and also, you can also return values in case of an error. So for example, to write readFileOrDefault (which returns a default value if for example the file doesn't exist) you would do this:
function readFileOrDefault(file, line, defaultContent) {
return fs.readFile(file).then(function(fileContent) {
return fileContent;
}, function(err) {
return defaultContent;
});
}You already have async.waterfall and async.auto with .then and .spread chaining.
files.getLastTwoVersions(filename)
.then(function(items) {
// fetch versions in parallel
var v1 = versions.get(items.last),
v2 = versions.get(items.previous);
return [v1, v2];
})
.spread(function(v1, v2) {
// both of these are now complete.
return diffService.compare(v1.blob, v2.blob)
})
.then(function(diff) {
// voila, diff is ready.
});
async.map is straightforward. // download all items, then get their names
var pNames = ids.map(function(id) {
return getItem(id).then(function(result) {
return result.name;
});
});
// wait for things to complete:
Promise.all(pNames).then(function(names) {
// we now have all the names.
});
If you want to wait for the previous item to download first (ala .mapSeries) thats also pretty straightforward: you just need the previous download to complete, then run the next download, then extract the item name, and thats exactly what you express in the code: // start with current being an "empty" already-fulfilled promise
var current = Promise.fulfilled();
var namePromises = ids.map(function(id) {
// wait for the previous download to complete, then get the next
// item, then extract its name.
current = current
.then(function() { return getItem(id); })
.then(function(item) { return item.name; });
return current;
});
Promise.all(namePromises).then(function(names) {
// use all names here.
});
The only thing that remains is mapLimit - which is a bit harder to write - but still not that hard: var queued = [], parallel = 3;
var namePromises = ids.map(function(id) {
// How many items must be complete before fetching the next?
// The queued, minus those running in parallel, plus one of the parallel slots.
var minComplete = Math.max(0, queued.length - parallel + 1);
// when enough items are complete, queue another request
// for an item, then get the item's name.
return Promise.some(queued, minComplete)
.then(function() {
var download = getItem(id);
queued.push(download);
return download;
}).then(function(item) {
return item.name;
});
});
Promise.all(namePromises).then(function(names) {
// use all names here.
});
Is a library needed? Probably, but only for mapLimit.