Promise with q framework and the callback pattern in Node.js?
asynchronous, node.js, promise, q
Solution
You have a few errors in your examples.
read1
This is not 'make a promise manually', this is just making a normal asynchronous call. In your code, you call `readFile` immediately, so `read1` would be the return value of `readFile` which is `undefined`. To get a behavior similar to `read2` and `read3` you would need to do something like this:
var read1 = function(fname, env, success, error){
fs.readFile(fname, enc, function (err, data) {
// Throwing here would just crash your application.
if(err) error(err);
// Returning from inside 'readFile' does nothing, instead you use a callback.
else success(data);
});
};
read2
// Not equivalent to read1 because of the notes above,
// Equivalent to read3, with the fixes I mention below.
var read2 = Q.nfbind(fs.readFile);
read3
var read3 = function (fname, enc) {
var deferred = Q.defer();
fs.readFile(fname, enc, function (error, text) {
if (error) {
// 'error' is already an error object, you don't need 'new Error()'.
deferred.reject(error);
} else {
deferred.resolve(text);
}
// HERE: Again returning a value from 'readFile' does not make sense.
return deferred.promise;
});
// INSTEAD: Return here, so you can access the promise when you call 'read3'.
return deferred.promise.
};
You can indeed use `nfbind` on anything that takes a callback as the last parameter. With my comments, `read2` and `read3` accomplish the same goal, which is to create a function that will take a filename and encoding, and return a promise object.
For those, you can do this:
read2('file.txt', 'utf8').then(function (data) {}, function (err) {}).done();
read3('file.txt', 'utf8').then(function (data) {}, function (err) {}).done();
For `read1`, you would call it like this:
read1('file.txt', 'utf8', function (data) {}, function (err) {});
Update
Standard promises have evolved a bit since this was answered, and if you are leaning toward `read3`, I'd recommend doing the following:
var read4 = function (fname, enc) {
return Q.promise(function(resolve, reject){
fs.readFile(fname, enc, function (error, text) {
if (error) {
// 'error' is already an error object, you don't need 'new Error()'.
reject(error);
} else {
resolve(text);
}
});
});
};
This is more in line with standard ES6 promises, and with bluebird, so you'll have an easier time with the code moving forward. Using the method mentioned in `read3` also introduces the possibility of synchronously throwing exceptions instead of capturing them in the promise chain, which is usually undesirable. See the deferred antipattern.
Problem
Even if well documented q framework is quite hard to understand if you are programming with Node.js for a few days. But I like to learn about it! ``` var Q = require('q'); var fs = require('fs'); // Make the promise manually (returns a value or throws an error) var read1 = fs.readFile(fname, enc, function (err, data) { if(err) throw err; return data; }); // Convenient helper for node, equivalent to read1? var read2 = Q.nfbind(fs.readFile); // Uh?! var read3 = function (fname, enc) { var deferred = Q.defer(); fs.readFile(fname, enc, function (error, text) { if (error) { deferred.reject(new Error(error)); } else { deferred.resolve(text); } return deferred.promise; }); }; // Execute Q.fncall(read1).then(function (data) {}, function (err) {}).done(); ``` Are `read1`, `read2` and `read3` equivalent? Can I use `Q.nfbind` every time the last parameter of a function accept a callback in the style of `function (err, value)`?