how to turn an async node function to a promise - node.js

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){});

Related

How to return promise's value, rather than promise itself in await/async?

I have the following code. Basically reading a file then return the content.
var result = await promise; return result;
Right now it returns promise itself. Is it a way to return the result straight away?
FileReader.prototype.readInputFile = async function(fileName) {
var condi = this.validateFileName(fileName);
if(condi == true) {
// wrap api, then wait
var promise = new Promise((res, rej) => {
fs.readFile(fileName, { encoding: 'utf-8' }, (err, data) => {
if(err) {
console.log(err);
rej();
}
res(data);
});
});
var result = await promise;
return result;
} else {
// not valid file
}
}
No you cannot. async function itself returns promise only. No matter what you return(or throw) in the body of function, it will eventually become a Promise.resolve() or Promise.reject().
When you call an async function the result is a promise. You can't expect an async function to return the value synchronously, right? So, the promise's value is returned properly. But the promise you see is the promise of the async function, not the one that was resolved inside it.
Put this console.log between these 2 lines and you would see it's an actual value:
var result = await promise;
console.log(result);
return result;
Now I understand, it will return promise instead, not result. I did find a way that it is able to return result, so the code can be nicely.
FileReader.prototype.readInputFile is an async func. If any non-async func calls it, the result will be promise. If any async func call this acync func. The result is actually data.

BlueBird Promises returns "Undefined" when return inside "then" function

Here is my Code:
Checking if user is folowing official Twitter Account (here I've returned new Promise
var isFollowing = function(sender) {
return new promise(function(resolve, reject) {
var following = false;
var params = {
source_id: sender,
target_screen_name: config.get('twitter.official_account')
};
TwitObj.get('friendships/show', params, function(err, data) {
if (!err) {
following = data.relationship.source.following;
resolve(following);
} else {
reject(err);
}
});
});
};
Validation:
var validateMsg = function(dm) {
var sender = getSender(dm);
var result = [];
isFollowing(sender.id)
.then(function(response) {
if (!response) {
result = interactiveMessage(false, lang.__('FOLLOW_MAIN', sender.name));
console.log(result); // Displays as expected
return result; // Not returning value Do I need to return promise again? If yes, WHY?
}
});
};
Main Implementation:
var direct_message = function(msg) {
verifyAccount().catch(function(err) {
console.error(err);
});
var dm = msg.direct_message;
var result = validateMsg(dm);
console.log(result);
};
Questions is how should I force validateMsg function to return result variable inside then function.
Update: While debugging, I got to know that, console.log(response) in
validation function is displaying later after throwing undefined in
"then" function which means program is not able to get response
immediately and due to async nature, I/O is not getting blocked. How
to tackle this?
You're not actually returning anything in the validateMsg function. You're returning a synchronous value ( result ) in the .then function which is a separate function.
The second thing to consider is that you're expecting the validateMsg function, which is asynchronous to behave synchronously.
var result = validateMsg(dm);
The way to achieve what I believe you're looking to do involves making the following changes:
1) Return the promise chain in the validateMsg function.
var validateMsg = function(dm) {
var sender = getSender(dm);
var result = [];
return isFollowing(sender.id)
.then(function(response) {
if (!response) {
result = interactiveMessage(false, lang.__('FOLLOW_MAIN', sender.name));
return result;
}
// Not sure what you want to do here if there is a response!
// At the moment you're not doing anything if the validation
// is successful!
return response;
});
};
2) Change your function call, since you're now returning a promise.
validateMsg(dm).then( function( result ){
console.log( result );
})
3) Consider adding a catch somewhere if only to help you debug :)
validateMsg(dm).then( function( result ){
console.log( result );
})
.catch( function( err ){
console.log( "Err:::", err );
});
The validationMsg function does not have a return value, thus the result in the main function has the value undefined. Try to put return in front of isFollowing so the function returns a promise, then treat validationMsg as a promise.

Async.series: Variable is undefined outside of function

I'm new to Node/Javascript and trying to grab a url audioLink by loading another url songLink. I would then add them to a new object rapSong. To achieve this I am using async series so that the calls will be happen asynchronously. I have tried other methods such as using async waterfall and just callbacks with no success.
audioLink is undefined unless I am within the getAudioLink function. Also the functions in the async.series do not run in the correct order. Any advice?
//for each song..
songLinkElem.each(function(i, s) {
var songName = StringUtils.removeWhiteSpacesAndNewLines($(s).children(".title_with_artists").text());
var songLink = $(s).attr("href");
var audioLink;
async.series([
function(callback){
function getAudioLink(songLink, callback){
request(songLink, function(err,resp,body){
$ = cheerio.load(body);
var audioElem = $(body).find(".audio_link > a");
var audioLink = audioElem.attr("href");
return callback(audioLink);
});
}
getAudioLink(songLink, function(resp){
audioLink = resp;
console.log("one");
})
console.log("two");
callback();
},
function(callback){
var rapSong = new Song(songName, artistLink, songLink, audioLink);
rapArtist.addSong(rapSong);
console.log("three");
callback();
}],
function(err, result){
console.log("four");
});
Within your first async.series function, you shouldn't call its callback until the callback for getAudioLink is called.
So move the code that follows the getAudioLink call inside its callback:
getAudioLink(songLink, function(resp){
audioLink = resp;
console.log("one");
console.log("two");
callback();
});
That way the async.series won't proceed until after audioLink is set.

make node.js fs.readFile as promised action using Q

when I use deferred.resolve way to promise a action,I can't get the content of the file
function readFile(fileName) {
var deferred = Q.defer();
fs.readFile(fileName, 'utf-8', deferred.resolve);
return deferred.promise;
};
readFile('test.txt').then(function (err, data) {
console.log('data:' + data)
})
I get data:undefined output
but it works OK fine when I promised action httpGet
var httpGet = function (opts) {
var deferred = Q.defer();
http.get(opts, deferred.resolve);
return deferred.promise;
};
httpGet('http://www.google.com').then(function (res) {
console.log("Got response: " + res.statusCode);
res.on('data', function (data) {
console.log(data.toString());
})
}
);
Is there something wrong the code above and in this way how can i get the content of the file.
or is there something different between fs.readFile and http.get?
You can use Q.nfcall to call a NodeJS function promisified.
function httpGet(opts){
return Q.nfcall(http.get,opts);
}
Or simply:
var httpGet = Q.nfbind(http.get,http)
This would also work for fs.readFile by the way.
If you want to do it manually. Q's deferred objects have a .makeNodeResolver function which lets you pass them around safely like you do:
var httpGet = function (opts) {
var deferred = Q.defer();
http.get(opts, deferred.makeNodeResolver());
return deferred.promise;
};
One of the things .makeNodeResolver does is bind the .this value.
It is better to use .nfcall though.

Promise with q framework and the callback pattern in Node.js?

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.

Resources