Callback is not a function for update in Node JS - javascript

begginer in Javascript and Node Js here.
While trying to do my first, simple update function, i got the error :
TypeError: callback is not a function.
I searched for the answer online but this problem is still a mistery.
function UpdateProductsCodes(columns, returnColumns, type, callback) {
for (var i = 0; i < columns.ids.length; i++) {
updateSql = "UPDATE TProductCodes SET code =?, product_id =? OUTPUT inserted.id, inserted.code, inserted.product_id INTO #returnValues WHERE ids =?";
var params = [];
params.push(columns.codes[i]);
params.push(columns.product_ids[i]);
params.push(columns.ids[i]);
sql.query(conn_str, updateSql, params, function (err, products, more) {
//Code stops here
//TypeError: callback is not a function
if (err) {
callback(err, null);
return;
};
if (!more) {
callback(null, products);
}
});
}
}
This function should do a simple update, nothing more. Its used here:
UpdateProductsCodes(req.body.entities, conditions, returnColumns, type, function (err, products) {
if (err) {
console.dir(err);
res.writeHead(500, { 'Content-Type': 'application/json' });
res.write(JSON.stringify(utils.GenerateResponse(err.message, true, 'JSON')));
res.end();
return;
}
res.writeHead(200, { 'Content-Type': 'application/json' });
res.write(JSON.stringify(utils.GenerateResponse(products, false, type)));
res.end();
});

The problem is that you are simply sending the wrong number of arguments when you call the function.
The function accepts four inputs: columns, returnColumns, type, callback. But in your example, you are sending five inputs: req.body.entities, conditions, returnColumns, type, function (err, products)
The last one (the function, in this case) is therefore ignored. The value which the function is receiving as being the callback value is in fact the one you've named type when you call the function, because that's the fourth argument you provide. This value is not an executable function - which is what the error message is telling you.
Now I don't know which values are the ones you actually need/want to send to the function, but clearly one of them is redundant and you need to remove it from the calling code. Based purely on the names, I'd guess that one of either req.body.entities or conditions is not needed, but of course I can't see what those variables contain, and I can't be certain of your intent, so you'll have to work it out yourself.
P.S. I also note that your function never actually uses the returnColumns or type parameters which it receives, so you maybe should consider whether you actually need to accept these at all. Perhaps they can be removed.

Related

Callback without effect, function still executed before callback

I have a function with a callback, my problem is that the return of the function happens before the callback ad then return null instead of returning me the array of coordinates
function callback(coordinates=[]){
console.log(coordinates);
return coordinates;
}
function getCoordinates(callback){
connection.connect();
let coordinates=[null];
connection.query('SELECT AreaId AS areaNumber, longitude AS longitude, latitude AS latitude FROM coordinates', function (error, results, fields) {
if (error) throw error;
let area=[];
for (result of results) {
if (!area.includes(result.areaNumber)){
area.push(result.areaNumber)
coordinates[result.areaNumber]=[];
}
coordinate=[result.longitude,result.latitude];
coordinates[result.areaNumber].push(coordinate);
}
coordinates=callback(coordinates)
});
connection.end();
return coordinates;
}
console.log(getCoordinates(callback));
and I have :
[null] //correspond to console.log(getCoordinates(callback));
and
[array with value] // corresponding to console.log(coordinates) in function vallback
How to do for that my callback will be considered?
Sorry I am kind of new on node.js so I may have missunderstood callback.
What I would like is to get an array of coordinates that I can use later on at 2 different places in my code like :
.get('/map', function(req, res) {
let Coords=getCoordinate(callback)
res.render('map.ejs', {token: tokenMapbox, coordinates: Coords});
})
.get('/wmap', function(req, res) {
let Coords=getCoordinate(callback)
res.render('wmap.ejs', {token: tokenMapbox, coordinates: Coords});
})
There are several approaches to this problem, however if you wish to use the resulting coordinates in a similar way to synchronous code, I suggest you try using the async/await syntax. If you call your query from an async function you can use the await keyword to provide more readable code.
Once you have your coordinates variable populated in your async function you can do what you wish with it.
For example:
function getCoordinates() {
connection.connect();
let coordinates = [];
return new Promise((resolve, reject) => {
connection.query('SELECT AreaId AS areaNumber, longitude AS longitude, latitude AS latitude FROM coordinates', function (error, results, fields) {
if (error) {
reject(error);
} else {
let area=[];
for (result of results) {
if (!area.includes(result.areaNumber)){
area.push(result.areaNumber)
coordinates[result.areaNumber]=[];
}
coordinate=[result.longitude,result.latitude];
coordinates[result.areaNumber].push(coordinate);
}
resolve(coordinates);
connection.end();
}
});
})
}
async function testCoordinates() {
let coordinates = await getCoordinates();
console.log("Coordinates:", coordinates);
// You can do whatever you wish with the coordinates variable
}
testCoordinates();
You're confusing different things. Let me explain
First thing, forget about the term "callback". We'll understand this simply as a function passsed as parameter to another function. Don't worry about it, I'm going to explain.
So we start from the problem: how to fetch array of coordinates from database and print it
Next thing you get to know about interacting with database and assuming this is how your library works: it has a function connection.query(myQuery, someFunction) which can get you results from database.
Now first thing you notice about that query function is it's parameters. myQuery is a string and someFunction is a function definition. While we have seen in other languages that we pass values such as numbers, string, etc. as parameters to a function, interestingly in javascript you can pass on a function definition as a parameter as well. And this is what makes javascript powerful, you can pass on function definition as parameter.
Wow, passing on function as parameter, but how does that work?
Let's take a different example; let's say here I want to create a function which will do some calculations
//Here myVar is a variable and doSomething is a function
function interestingJsFunction(myVar, doSomething){
var twiceOfVar = 2*myVar;
doSomething(twiceOfVar);
var thriceOfVar = 3*myVar;
doSomething(thriceOfVar);
}
So what does this function do? It applies some calculations and calls the function doSomething at some points; at one point it passes twice value of myVar to function and at one point it passes thrice of myVar. But what this function doSomething do is not defined as of now. And you can define it by passsing your own function as parameter. This gives you infinite possibilities. How?
interestingJsFunction(2, function(result){ console.log(result) })
interestingJsFunction(2, function(result){ console.log("the new behaviour" + result) })
//And so on...
You understand how this has made one interestingJsFunction useful in different cases. If you knew that you would need only one implementation of doSomething(), you could have simply removed the doSomething from parameters and whatever you wanted to do, you could have done that directly inside interestingJsFunction(e.g. console.log(twiceOfVar))
Now coming back to the query function. It is also an interestingJsFunction and if you go to definition of query function(you can dig up the code of your library to see what's inside query function) you'll find that it does some operations on database, gets the results and call the function[similar to doSomething] which was passed as parameter, if it gets any error while doing so, it calls the function in parameter and passes error to it.
When it does so, it sends back error, results and field as parameter to this function. So now you can utilize these paramaeters in your function which was passed as parameter. It's upto you now how you want to utilize this, it could be that you can simply print the results as following (remember how we used interestingJsFunction?)
//We could simply print the results if there are any
connection.query(myQuery, function(error, results, fields){
if(results) console.log(results.toString())
})
//Or we could throw erorr if there's any
connection.query(myQuery, function(error, results, fields){
if(error) throw error
})
//Guess what would happen when you return something inside this function?
var myQueryFunction = connection.query(myQuery, function(error, results, fields){
return "return of query function"
})
//And guess what would happen when you have something like this
function myNewFunction(){
var x = connection.query(myQuery, function(error, results, fields){
return "return of query function"
})
return "return of myNewFunction"
}
I leave last 2 exercises for you and you should be able to fix the problems after that. Try console.log statements to understand

Get Value from parameter function

Sorry for the Noob Question. I'm trying to write a node.js function called "getTransitionId" that uses the jira-connector plugin to retrieve data for possible transitions for a particular issue.
The getTransitions function from the jira-connector takes two parameters: an object with the parameters of the ticket, and a function to execute once the call is finished.
Here's my problem: for reasons beyond the scope of this question, I want to access this data outside the function that's being passed as parameter to "getTransitions." But I can't figure out how. I understand that the last return statement (return "transitionData") is returning "undefined" because it's executing a return statement before the call is finished, but I don't know how to fix that.
Can I correctly use a callback in this case? If so, how would I use it in a function that is being passed as a parameter to another function?
const JiraApi = require('jira-connector');
const jira = new JiraApi( {
host: //Jira URL
basic_auth: {
//Authentication Information
}
});
function getTransitionId (ticketNumber, transition) {
jira.issue.getTransitions({
issueKey: ticketNumber,
}, function(error, transitions){
const transitionData = transitions['transitions'];
});
return transitionData;
}
Thanks for the help. Hope this made sense.
You could make your own getTransitionId function take a callback function as an argument. Here's an incomplete example (see ahead):
function getTransitionId (ticketNumber, transition, callback) {
jira.issue.getTransitions({
issueKey: ticketNumber,
}, function(error, transitions){
const transitionData = transitions['transitions'];
const id = /* ..get ID fron transitionData, somehow.. */
callback(id);
});
}
// Called like this:
getTransitionId(ticketNumber, transition, function(id) {
console.log("Got the ID:", id);
});
This isn't perfect, though. What if getTransitions has an error?
When you call jira.issue.getTransitions, you pass a callback function which takes two parameters: error and transitions. This is standard for functions which take callbacks in JavaScript - that is, callbacks usually take an error parameter (null or undefined if there was no error) and a data parameter (containing results of the action, like fetched transitions or an id).
We can change getTransitionId to take an error and then pass the error to the callback that you gave to getTransitionId:
function getTransitionId (ticketNumber, transition, callback) {
jira.issue.getTransitions({
issueKey: ticketNumber,
}, function(error, transitions){
if (error) {
callback(error);
return;
}
const transitionData = transitions['transitions'];
const id = /* ..get ID fron transitionData, somehow.. */
callback(null, id);
});
}
(Note that we use a return; statement inside if (error) -- that's so that we don't continue and try to use the transitions argument, which is probably undefined, since there was an error in jira.issue.getTransitions. This also prevents callback from being called a second time.)
Since we've added an error argument, we need to change how we call getTransitionId:
getTransitionId(ticketNumber, transition, function(error, id) {
if (error) {
console.error("There was an error fetching the transition ID:", error);
return;
}
console.log("Got the ID:", id);
}
(Since we do callback(null, id); in the code for getTransitionId, error will be null, so the code in if (error) { won't run. Of course, if there is an error, if (error) { will be run, and that's what we want!)
By adding code to handle errors passed to your callbacks, you make your code safer. Contrastingly, if you ignore error, you might end up having errors in your code that are hard to find - for example, you might have a TypeError because transitions is undefined, or see "Got the ID: undefined" in the log!

Async Javascript for loop

I have a problem with my for loop only one if block works.
I have to get the temperature from a whether API and put it back my table using my put method for every city.
The problem is that I can't fill all the cities.
How can i use ASYNC (https://caolan.github.io/async) module to make for loop work?
Here is my code :
for (i in guests) {
ville = i[ville_id];
guest_id = i._id;
if (ville) {
results.push(req2 = http.get('http://api.openweathermap.org/data/2.5/weather?q=' + ville + ',france&APPID=xxx', function(res) {
return res.on('data', function(chunk) {
var options, test, url;
test = chunk.toString();
obj = JSON.parse(test);
res = obj.main.temp;
i[temp_id] = res;
url = update_guest + guest_id.toString();
options = {
method: 'PUT',
url: update_guest + guest_id,
qs: {
api_key: 'xxx'
},
headers: {
'Cache-Control': 'no-cache',
'Content-Type': 'application/json'
},
body: {
'595f9b2a5ea9cb0004c21290': res
},
json: true
};
return request(options, function(error, response, body) {
if (error) {
throw new Error(error);
}
return console.log(body);
});
});
}));
}
}
This is a pretty common issue in JavaScript due to the way closures work. What is happening is your loop for (i in guests) is completed before any of your callbacks (function(res){...}) have a chance to run. Your callbacks reference variable i, but they don't "remember" what the value of i was when you made your HTTP request. Instead, when your callbacks execute, i will be whatever the last value in guests is since your for...in loop would have already completed by the time your callbacks are executed.
For example, let's say guests has two keys in it, guest1 and guest2. Here's what would happen:
The first iteration of your loop runs. i is now guest1. The first HTTP request is fired.
The second iteration of your loop runs. i is now guest2. The second HTTP request is fired.
Both HTTP requests complete and your callbacks are executed, but i is set to guest2. As such, both callbacks references to i will be guest2.
What you need to do is find a way to give your callbacks access to the value of i at the time your request was fired. You could do this by creating your callbacks with a function that takes i as a parameter like so:
for (i in guests) {
ville = i[ville_id];
guest_id = i._id;
if (ville) {
results.push(req2 = http.get('http://api.openweathermap.org/data/2.5/weather?q=' + ville + ',france&APPID=xxx', (function (localI) { return function(res) {
return res.on('data', function(chunk) {
// removing code for clarity
localI[temp_id] = res;
// removing code for clarity
return request(options, function(error, response, body) {
// removing code for clarity
});
});
})(i)));
}
}
You can read more about this approach here.
What I've done is created a function which returns your callback and takes a parameter localI. I then immediately passed i as that parameter, returning a callback with localI set to the current value of i. Then I changed your reference to i inside the callback to localI. Note that your references to ville and guest_id inside your callback will also have the same issue as i. You'll either need to pass those as parameters as well or declare them inside your callback.

get object of returned mongoose find

Hi i'm trying to access the elements returned from a find in mongoose and having some trouble with the asynchronous and callback situation.
Here is the code for better understanding.
function retrieveBudgets(email, callback) {
models.User.find({email: email},{budget:true}, function(err,budgets) {
if (err) {
callback(err, null);
} else {
callback(null, budgets);
}
});
};
retrieveBudgets(user.email, function(err, budgets) {
if (err) {
console.log(err);
}
budgets.forEach(function(budget){
console.log(JSON.stringify(budget, null, 4));
});
});
So this line console.log(JSON.stringify(budget, null, 4)); is working correctly and printing the objects to screen in json format but how do I store each to an array of objects from here? if I try to push to an array at this same line I get an error.
I have seen some questions that are similar but i am not getting any headway with them.
EDIT:____________________________________________________________
I did a little hack to get it working, i moved res.render up, so that rendering the page was done at the same time as the callback but I cant see this being the right solution any thoughts
var user=req.session.user;
res.locals.budgets=[];
function retrieveBudgets(email, callback) {
models.User.find({email: email},{budget:true}, function(err, budgets) {
if (err) {
callback(err, null);
} else {
callback(null, budgets);
}
});
};
retrieveBudgets(user.email, function(err, budgets) {
if (err) {
console.log(err);
}
res.locals.budgets = budgets.map((function(b){ return b; });
res.render('budget/budget.jade',{ csrfToken: req.csrfToken() });
});
This works I can access budgets through locals so any feedback on this would be great I doubt its the right way to do it?
the budgets return value that you get from the retrieveBudgets call is already an array.
this is evidenced by your call to budgets.forEach which is a method on arrays.
is there a specific need to create a new array from the items? that can be easily done:
var myNewArray = budgets.map((function(b){ return b; });
this one line of code will map the original budgets array into a new array containing each of the budget items.
there are other methods of creating a new array, depending on what you need to do exactly
update from comments below
what i really want to do to is use the budgets outside of the query so I can pass it to the view
in that case, you need to render the view from within the callback and pass the budgets to the view:
router.get("/foo", function(req, res, next){
retrieveBudgets(user.email, function(err, budgets) {
if (err) { return next(err); }
res.render('budget/budget.jade',{
budgets: budgets,
csrfToken: req.csrfToken()
});
});
});
This is the only, and correct, way to make this work.
If you tried to do it without waiting for the callback to finish, you would not have any data in your budgets array. Therefore, you must wait for the callback to be executed and then render your view with the budgets (or single budget or whatever) passed to the view.
(There are variations of this using promises, but I find callbacks to be the easier way to handle this.)
Turn the line in which you're passing an object to your jade file
res.render('budget/budget.jade',{
csrfToken: req.csrfToken(),
budgets: budgets.map(function(b) {return b;})
});
This will pass budgets to your jade file, and you should be able to access it there.

Node.js 'Not Found' callback for async.detect?

I have a file path list file_paths, and I want to detect which file exists.
If any file exists, I want to read that one. Otherwise call another function,
not_found for example.
I wish to use async.detect but I found no way to add a 'Not Found' callback
when all the iterators return false.
I've tried this one, but no work. Returned undefined and nothing outputted.
async = require 'async'
async.detect [1,2,3], (item, callback) ->
callback true if item == 4
, (result) ->
console.log result ? result : 'Not Found'
If there's another way to do it, please add it to the answer.
from the documentation you mentioned.
in case of detect(arr, iterator, callback)
callback(result) - A callback which is called as soon as any iterator
returns true, or after all the iterator functions have finished.
Result will be the first item in the array that passes the truth test (iterator) or the value undefined if none passed.
from your question you want to find a way to detect if no file in list is found, which could be done by comparing the result with undefined and checking whether this condition is true.
like
async.detect(['file1','file2','file3'], fs.exists, function(result){
if(typeof(result)=="undefined") {
//none of the files where found so undefined
}
});
I would use async.each and use fs.exists to detect if the file exists. If it exists, then read the file otherwise, call the not found function then proceed to the next item. See sample code I have written on top of my head below.
async.each(file_paths, processItem, function(err) {
if(err) {
console.log(err);
throw err;
return;
}
console.log('done reading file paths..');
});
function notFound(file_path) {
console.log(file_path + ' not found..');
}
function processItem(file_path, next) {
fs.exists(file_path, function(exists) {
if(exists) {
//file exists
//do some processing
//call next when done
fs.readFile(file_path, function (err, data) {
if (err) throw err;
//do what you want here
//then call next
next();
});
}
else {
//file does not exist
notFound(file_path);
//proceed to next item
next();
}
});
}

Categories

Resources