I'm sorry, but:
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;
});
}