node kriskowal/q promise stack errors on chaining promises - node.js

The following code errors out on the first .then with:
/Users/danielbyrne/working/mite_npm/mite.js:48
.then(configMan.validateSettings(parsedData))
ReferenceError: parsedData is not defined
I don't understand why this code is failing.
The call:
parseConfigurationFile(path.join(__dirname,'file.config'))
.then(validateSettings(parsedData));
The functions it calls:
function parseConfigurationFile(fileName) {
var readFile = q.nfbind(fs.readFile);
readFile(fileName,"utf-8")
.then(function(data) {
var deferred = q.defer();
// Return the Config 'settings' in JSON
deferred.resolve(JSON.parse(data));
return deferred.promise;
});
}
function vaidateSettings (data) {...}
The only way this works is if I change the function validateSettings to an anonymous function and place it inline like this :
parseConfigurationFile(path.join(__dirname,'file.config'))
.then(function(parsedData){...});
Why can i not chain named functions in this manner?

Your validateSettings call should be like this:
parseConfigurationFile(path.join(__dirname,'file.config'))
.then(validateSettings);
The reason is that the validateSettings needs to be referenced as a function and the .then will invoke that function with the correct parameter. Doing it the way you are doing it, you receive a reference error because parsedData is not available at the time the function call is bound.

Related

client.on is not a function (not discord.js related)

I'm creating my own package that needs to do some emitting from the package to the main file.
This simplified module gets what I'm trying to achieve:
const fetch = require("node-fetch")
module.exports.client = async function(username,password,version) {
// Do login code here
this.emit("login", loginData)
}
Then my test code runs that with
var client = new require("../index").client("username","password","0.774")
client.on("login",function(data) {
console.log(data)
})
With every time, the program returning
client.on("login",function(data) {
^
TypeError: client.on is not a function
Has anyone had a similar issue? This is my first time making a module in this style, so please let me know.
Thank you!
Because your function client() exported from index.js is declared with the async keyword, it will return a Promise rather than an actual value passed to the return statement. Assuming the value you return from the function client() is an object with an on() function, then in order to access this function you either need to await the result of client() or you need to use the .then() function on promise. Example:
var clientPromise = new require("../index").client("username","password","0.774")
clientPromise.then(client => {
client.on("login",function(data) {
console.log(data)
})
});
While you could also use await on the result of the client() function, this is not allowed in all contexts, and so you would have to wrap the code that does this in another async function.

Promise function not returning results

I'm using instagram-scraping module to display all posts with a specific hash tag but I get an issue using a function to return finded items.
// in my middleware
function getInstagramPostsByHashTag(hashtag) {
return ig.scrapeTag(hashtag).then(result => {
console.log(result); // all posts are displayed in my console
return result;
});
}
// in my ejs template i send the function getInstagramPostsByHashTag()
<%=getInstagramPostsByHashTag("instagram")%> // nothing is displayed
You can't return the posts from the function because the function has already returned at this point. That's because .then() is asynchronous. It executes the provided callback when the work (fetching the posts) is done, but the function continues to run after the call to .then(), and because you return nothing, you get nothing.
If you want a function to return the result of an asynchronous operation, you have to return a promise from the function itself. To help developers with that, there's the async function that automatically returns a promise. In an async function, you can wait for other promises with the await keyword. Your function would look like this as an async function:
async function getInstagramPostsByHashTag(hashtag) {
return await ig.scrapeTag(hashtag)
}
But this is redundant because it returns exactly the same as a direct call to ig.scrapeTag(hashtag) would do. I don't really know ejs but I think the best thing you could do is something like this (only pseudocode, again, I don't know how rendering with ejs works):
posts = insta.scrapeTag("blablabla")
then
ejs.render("<%=posts%>", {posts})

Node Js passing parameters to an "off the shelf" callback

I have a library that makes a web request and passes the results to a callback like the sample below:
someReadyWebSearchThirdPartyLibrary.getSearch(parameters, error, callback);
Then the callback is something like:
var callback = function (data) {
console.log(data);
};
Is there a way I can pass more arguments to the callback from the caller given this is an off the shelf ready library?
just define your "first" callback inline, there you can pass more parameters:
someReadyWebSearchThirdPartyLibrary.getSearch(parameters, error, function(data) {
callback(data, otherVar1, otherVar2);
});
Then your callback could look something like this:
var callback = function (data, otherVar1, otherVar2) {
console.log(data);
console.log(otherVar1);
console.log(otherVar2);
};
Without editing the code of the library that calls the callback, you can't change what is passed to that specific callback. But, there are multiple options where you can get access to other variables:
Inline callback accessing variables in scope
you can use scope and an inline definition to make other variables available (via scoping) to the callback.
let someVar1 = "foo";
let someVar2 = 4;
someReadyWebSearchThirdPartyLibrary.getSearch(parameters, error, function(data) {
// can access data, someVar1 and someVar2 here as they are all in scope
console.log(someVar1);
});
Use .bind() to add arguments to the callback
You could also use .bind() to create a new function that adds parameters.
function callback(someVar1, someVar2, data) {
console.log(someVar1);
}
someReadyWebSearchThirdPartyLibrary.getSearch(parameters, error, callback.bind(null, "foo", 4));
.bind() creates a new function that adds additional arguments before calling the actual callback() function.
Make your own stub function
Or, make your own stub function that calls your callback (essentially what the .bind() option was doing for you):
function mycallback(someVar1, someVar2, data) {
console.log(someVar1);
}
someReadyWebSearchThirdPartyLibrary.getSearch(parameters, error, function(data) {
mycallback("foo", 4, data);
});
I assume your callback arguments are variable and that the function is defined in an isolated manner (where it can't access scope beyond itself, and that you don't want to mutate the callback function's underlying data. It also assumes that Data is an object (You should perform some validation on this though). Therefore you could do something like the following:
someReadyWebSearchThirdPartyLibrary.getSearch(parameters, error, function(data) {
data.newArgument = 'lorem';
callback(data);
});
This way you can add any arbitrary arguments onto the data object passed.

Can't call nodeify on async function but can call then()

I have these 2 functions.
async function testAsync(){
console.log('async func')
}
function callback(){
console.log('callback function.')
}
When I call then on async it works, similar to the promise. For example, the following code works.
bluebird.resolve().then(()=> console.log('then promise'))
testAsync().then(()=> console.log('then async'))
But when I call nodeify on async then it gives error. Although, it works for promise
Works ->bluebird.resolve().nodeify(callback)
Error -> testAsync().nodeify(callback)
This is the error that I got. Why so?
TypeError: testAsync(...).nodeify is not a function
The error states that there is no nodeify function on the native promise object.
If you need to use this function, which you really shouldn't, you have to get it from the promise implementation that defines it and call it on the object something like p.prototype.nodeify.call(testAsync()) and then hope that it does not use any internal parts of the promise implementation.
Or better extract the function from the promise implementation because it is only something like this:
function nodeify(cb) {
this.then(function (v) { cb(null, v) });
this.catch(function (err) { cb(err) });
}
which can be used as nodeify.call(testAsync(), callback)

bluebird .all() does not call .then

I have an unknown number of async processes that might run from a request. There is a block of text to be modified by these processes.
UpdateScript is called with the text to be modified, it has a callback that I would like to run when everything is complete.
var promise = require('bluebird');
function updateScript(text, cb){
var funcChain = [],
re = some_Regular_Expression,
mods = {text: text};
while (m = re.exec(mods.text)) {
// The text is searched for keywords. If found a subprocess will fire
....
funcChain.push( changeTitleAsync(keyword, mods) );
}
promise.all(funcChain)
.then(function(){
// This is never called.
cb(mods.text);
});
}
function changeTitle(encryptedId, mods){
try{
// database request modifies mods.text
}catch(e){
throw e;
}
}
var changeTitleAsync = promise.promisify(changeTitle);
The changeTitle code is called but the "then" call is not
Partly the problem is incorrect use of promisify(). This is meant to convert a node-style function that takes a callback as it's last argument into one that returns a promise (see docs here).
Instead what you can do with your function above is have it return a new Promise manually like this:
function changeTitle(encryptedId, mods) {
return new Promise(function(resolve, reject){
try {
// do something then resolve promise with results
var result = ...
resolve(result)
} catch (e) {
// reject the promise with caught error
reject(e)
}
})
}
HOWEVER
There is one mistake above: I assume the db call to update the text is also asynchronous, sothe try /catch block will never catch anything because it will run through just as the db kicks off intially.
So what you would have to do is promisify the DB call itself. If you're using a node db library (like Mongoose, etc) you could run Promise.promisifyAll() against it, and use the Async versions of the functions (see promisifyAll section on above link for details).
Hope this helps!!

Resources