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)
?
You have a few errors in your examples.
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);
});
};
// Not equivalent to read1 because of the notes above,
// Equivalent to read3, with the fixes I mention below.
var read2 = Q.nfbind(fs.readFile);
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) {});
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With