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.
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.
Related
I'm very new to async languages like nodejs, I'm trying to write a web scraper that will visit a link, download a code, extract with regex, THEN visit another link using that code. I'm aware I could use callbacks, but I expect to have to go 8-9 levels deep, I think promises is the way to go (is there a better way?)
var promise = require("promise");
var request = require("request");
login();
function get_login_code()
{
request.get("someurl.com", function (error, response, body)
{
// just for example
body = 'TOKEN" value="hello world"';
var login_code = body.match(/.TOKEN" value="([^"]+)../i);
return login_code
});
}
function login()
{
var login_promise = promise.resolve(get_login_code());
console.log(login_promise);
}
I've tried a bunch of combinations of messing around with promises, but I either always get undefined or a promise which doesn't have a value. I don't want to nest promise functions inside promises because that's exactly the same thing as callback hell. Can someone tell me what I'm doing wrong, I really want this code to be procedural and not 8 callbacks. In the ideal world promise.resolve just waits until get_login_code() returns the actual code, not undefined.
Output:
Promise { _45: 0, _81: 1, _65: undefined, _54: null }
Desired Output:
hello world
What your code do:
calls get_login_code that returns nothing (i.e. undefined)
inside of login function you create a new promise that is immediately resolved to the result of get_login_code, i.e. undefined.
Thus, you do not use login_code at all.
To make it work, you should make get_login_code to return a promise that will be resolved to login_code. Consider you use promise npm module, the code may look like:
// uppercased, it's a constructor
var Promise = require("promise");
var request = require("request");
login();
function get_login_code()
{
return new Promise(function (resolve, reject) {
request.get("someurl.com", function (error, response, body) {
if (err) {
reject(err);
return;
}
// just for example
body = 'TOKEN" value="hello world"';
var login_code = body.match(/.TOKEN" value="([^"]+)../i);
resolve(login_code);
});
});
}
function login()
{
// return a new promise to use in subsequent operations
return get_login_code()
.then(function(login_code) {
console.log(login_code);
});
}
You should create new promise in the function to handle reject and resolve not by handling resolve to the function itself. Use then to get the response value from promise. I guess this should work.
var promise = require("promise");
var request = require("request");
function get_login_code()
{
var promise = new Promise(function(resolve, reject) {
request.get("someurl.com", function (error, response, body)
{
if (error) {
reject(error);
} else {
// just for example
body = 'TOKEN" value="hello world"';
var login_code = body.match(/.TOKEN" value="([^"]+)../i);
resolve(login_code);
}
});
});
}
get_login_code()
.then(function (code) {
console.log(code);
});
I am using bluebird for promises.
I am trying to promisify the download module.
Here is my implementation:
Promise = require('bluebird'),
download = require('download');
var methodNameToPromisify = ["download"];
function EventEmitterPromisifier(originalMethod) {
// return a function
return function promisified() {
var args = [].slice.call(arguments);
// Needed so that the original method can be called with the correct receiver
var self = this;
// which returns a promise
return new Promise(function(resolve, reject) {
// We call the originalMethod here because if it throws,
// it will reject the returned promise with the thrown error
var emitter = originalMethod.apply(self, args);
emitter
.on("response", function(data) {
resolve(data);
})
.on("data ", function(data) {
resolve(data);
})
.on("error", function(err) {
reject(err);
})
.on("close", function() {
resolve();
});
});
};
};
download = { download: download };
Promise.promisifyAll(download, {
filter: function(name) {
return methodNameToPromisify.indexOf(name) > -1;
},
promisifier: EventEmitterPromisifier
});
Then using it:
return download.downloadAsync(fileURL, copyTo, {});
My problem is that it doesn't download all of the files (I have a list sent to this function), what am I doing wrong?
An emitter does emit multiple data events, one for every chunk it receives. However, a represents only one future value, in your case you want that to be the complete response.
resolve is supposed to be called only once, to fulfill the promise with the passed value, which is then settled. Further calls will have no effect - and that's why you get only the first parts of your list.
Instead, you will need to accumulate all the data, and when the stream ends you can fulfill the promise with all of it.
var Promise = require('bluebird'),
download = require('download'),
Buffer = require('buffer'); // should be global anyway
exports = {
downloadAsync: function promisifiedDownload() {
var args = arguments, self = this;
return new Promise(function(resolve, reject) {
// We call the originalMethod here because if it throws,
// it will reject the returned promise with the thrown error
var emitter = download.apply(self, args);
var buffers = [];
emitter.on("data", function(data) {
buffers.push(data);
}).on("error", function(err) {
reject(err);
}).on("close", function() {
resolve(Buffer.concat(buffers));
});
});
};
};
Notice it's quite nonsensical to use promisifyAll when you only want to promisify a single method. I've omitted it for simplicity
You might also listen for the incoming response object, and attach the data listener directly to it. You can then use the end event instead of close.
I'm using the Q library and async library in nodejs.
Here's an example of my code:
async.each(items, cb, function(item) {
saveItem.then(function(doc) {
cb();
});
}, function() {
});
saveItem is a promise. When I run this, I always get cb is undefined, I guess then() doesn't have access. Any ideas how to work around this?
Your issue doesn't lie with promises, it lies with your usage of async.
async.each(items, handler, finalCallback) applies handler to every item of the items array. The handler function is asynchronous, i.e. it is handed a callback, that it must call when it has finished its work. When all handlers are done, the final callback is called.
Here's how you'd fix your current issue:
var handler = function (item, cb) {
saveItem(item)
.then(
function () { // all is well!
cb();
},
function (err) { // something bad happened!
cb(err);
}
);
}
var finalCallback = function (err, results) {
// ...
}
async.each(items, handler, finalCallback);
However, you don't need to use async for this particular piece of code: promises alone fill this job quite nicely, especially with Q.all():
// Create an array of promises
var promises = items.map(saveItem);
// Wait for all promises to be resolved
Q.all(promises)
.then(
function () { // all is well!
cb();
},
function (err) { // something bad happened!
cb(err);
}
)
I have this async function that I want to turn into a promise
var myAsyncFunction = function(err, result) {
if (err)
console.log("We got an error");
console.log("Success");
};
myAsyncFunction().then(function () { console.log("promise is working"); });
and I get TypeError: Cannot call method 'then' of undefined.
What is wrong with this code?
There are various ways in Q:
Q.nfcall(myAsyncFunction, arg1, arg2);
Q.nfapply(myAsyncFunction, [arg1, arg2]);
// Work with rusable wrapper
var myAsyncPromiseFunction = Q.denodeify(myAsyncFunction);
myAsyncPromiseFunction(arg1, arg2);
in Deferred implementation:
var myAsyncPromiseFunction = deferred.promisify(myAsyncFunction);
myAsyncPromiseFunction(arg1, arg2);
One notable difference: Wrappers as generated by Deferred additionally auto-resolve promises passed as an arguments, so you can do:
var readFile = deferred.promisify(fs.readFile);
var writeFile = deferred.promisify(fs.writeFile);
// Copy file
writeFile('filename.copy.txt', readFile('filename.txt'));
myAsyncFunction return nothing(undefined actually) in your code.
If you use whenjs, the normal way will be like this:
var myAsyncFunction = function() {
var d = when.defer();
//!!!do something to get the err and result
if (err)
d.reject(err);
else
d.resolve.(result);
//return a promise, so you can call .then
return d.promise;
};
Now you can call:
myAsyncFunction().then(function(result(){}, function(err){});
If I have the following code:
var content;
var f = fs.readFile('./index.html', function read(err, data) {
if (err) {
throw err;
}
content = data;
return console.log(content);
});
f();
I got the following:
f();
^
TypeError: undefined is not a function
How can I do to return a function, which is not undefined?
My real issue, in a bigger context is the following https://gist.github.com/jrm2k6/5962230
You can't usefully return from within the callback to your code.
Using return inside the callback doesn't return a value to your code. fs.readFile() calls the callback itself so the return value is given to it. For your code to receive it, fs.readFile() would have to return the value it got from the callback, which it doesn't.
And, since fs.readFile() is asynchronous, it actually can't. Your code spans 2 different points in time:
"Present"
var content;
var f = fs.readFile('./index.html', /* ... */);
f();
"Future"
/* ... */
function read(err, data) {
if (err) {
throw err;
}
content = data;
return console.log(content);
}
/* ... */
You simply can't use a value from the "Future" in the "Present" -- or, at that point, the "Past."
This all generally leads to continuing the callback pattern rather than using return. Taking your "real" code:
var cheerioURLContent = function (url, callback) {
rest.get(url).on("complete", function (result) {
callback(cheerio.load(result));
});
};
cheerioURLContent('./index.html', function (t) {
console.log(t);
});
You can assign event handlers for custom events and trigger (emit) the events from within the callback. It's a bit hard to get your head around at first, but pretty elegant.
See Emitting event in Node.js for an example.