I have an api endpoint called status. Which should be used like this:
status/ohio/columbus
status/nebraska/fremont
status/ohio/columbus/police
status/nebraska/fremont/fire
The state and city paths are mandatory but the department is not.
I'd like to have a function which updates a callback with the return value. Is the following function reasonable or confusing? Should I have multiple functions instead - getStatus and getStatusForDepartment?
function getStatus(state, city, department, callback)
{
let status = "status/"+state+"/"+city
if(typeof department != "function"){
status = status+"/"+ department
}else{
callback = department
}
...
}
Your approach is totally fine and a common pattern. Another option would be to have your function return a Promise, rather than relying on a callback. This would allow you to bypass checking if the last argument is a function or callback
function getStatus(state, city, department) {
return new Promise((resolve, reject) => {
let status = "status/"+state+"/"+city
if (department) {
status += '/' + department;
}
...
if (successThingHappens) {
resolve(data);
} else {
reject(error);
}
});
}
getStatus('CA', 'San Francisco').then((data) => {
console.log('do something with', data);
}).catch((err) => {
console.error('something went wrong', error);
});
I did not understand the part regarding the "multiple functionss instead".
But about the requirement of having optional argument in the middle of the parameter list, I follow the object approach.
Instead of doing
getStatus(state, city, department, callback)
I do like this
var arg_list = {"state": state, "city": city, "department": department, "callback": callback};
getStatus(arg_list);
And within the function, assign the value from object to the varables.
This way you can omit and use any parameter.
This approach has one very good benefit. We often need to make changes to the parameter list. Sometime add new one where as sometime remove some from the parameter list and sometime we need to change the order etc. All these operations need to be done very carefully when using the parameters since this may make the function definition different to the function call. But with this approach such cases can be easily handled.
Related
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
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.
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!
I am working on a database manager for an app. At a certain point I want to save some data like this:
//call the saveBillEvent
DatabaseManager.saveBillEvent(model, callbackfunc);
//accepts billeventmodel object
DatabaseManager.prototype.saveBillEvent = function(billEventModel, callback){
db.transaction(
RekeningDelen.DatabaseManager.databaseInstance,
RekeningDelen.DatabaseManager.saveBillEventSQL,
function(error) {
console.log(error);
console.log('transaction failed billeventspersons table creation ');
},
function(transactionId, result) {
console.log("transcation success billeventspersons, set firstappboot to false");
store.setItem("firstAppBoot", "false");
}
);
}
The saveBillEvent contains a transaction which at a given moment calls the saveBillEventSQL.
DatabaseManager.prototype.saveBillEventSQL = function(billEventModel, callback) {
//here i need the billEventModel to create the SQL
db.executeSql(
transactionId,
getAllBillEventsSQL,
null,
function(transactiondId, results) {
//here i want to call the callback
console.log('billevent saved ' + results);
},
function(response) {
alert('fail1');
console.log("SELECT billEvent query failed " + response);
}
);
}
This function contains the final callback, which should call the passed callback, for a certain query and also needs the billEventModel to create the query. Thus the billEventModel and the callback should be passed to this function, but that's not possible since the transaction triggers it at a specific moment.
So my question is how to deal with this A(with params)->B(has params, but cant pass through)->C(needs params) problem?
I hope you all can understand my question, if not add a comment.
I have a hash that calls a function to get a value. The problem is, the function is returning the function inside rather than the values it should.
(user is defined above this hash)
My hash:
userInfo = {
id: user.id,
email: user.email,
cars: getCars(user.id),
}
Which calls this function:
getCars = (userId) ->
id = parseInt(userId)
userRef = new Firebase("https://demo-firebase.firebaseIO.com/users/#{id}/")
userRef.on('value', (snapshot) ->
if snapshot.val() == null
["toyota"]
else
snapshot.val().cars # returns an array of cars
)
When I'm in the debugger and stepping through the function, it returns on the userRef.on line rather than the correct place in the if/else statement.
Here's the compiled JS:
getCars = function(userId) {
var id, userRef;
id = parseInt(userId);
userRef = new Firebase("https://demo-firebase.firebaseIO.com/users/" + id + "/");
return userRef.on('value', function(snapshot) {
if (snapshot.val() === null) {
return ["toyota"];
} else {
return snapshot.val().cars;
}
});
};
Any ideas why this is happening? I'm sure it's something simple I'm overlooking.
So the data you are getting from firebase is event-driven and asynchronous, so you can't just return it as if this was synchronous code. You need to use a either a callback, a promise, or event handler.
getCars = (userId, callback) ->
id = parseInt(userId)
userRef = new Firebase("https://demo-firebase.firebaseIO.com/users/#{id}/")
userRef.on 'value', (snapshot) ->
if snapshot.val() == null
callback ["toyota"]
else
callback snapshot.val().cars # returns an array of cars
userInfo =
id: user.id
email: user.email
getCars user.id, (cars) ->
userInfo.cars = cars
#Don't user userInfo until here as it's not ready/populated yet!
(Note the node convention is callback(errorOrNull, value), but I'm omitting error handling here for simplicity)
Also note that almost everyone new to async javascript makes this mistake, but it's not a simple syntax gotcha, it's a fundamental thing you at some point (maybe today) you will have the aha/lightbulb moment. The thing to do is step through this in the chrome debugger and note the order each line of code executes in relationship to time. The line with the if statement executes LATER IN TIME after getCars has already returned. And note if you step through it it will skip right over the body of the 'value' event handler because that line just DEFINES the event handler, but it doesn't actually EXECUTE it until the data arrives, so if you want to debug in that, you need to set a breakpoint on the first line of that function (where the if statement is).
There are 3 common paradigms available for this: event binding, promises, and callbacks. All will work. It would be a good exercise for you to code this same functionality with each paradigm and understand that they all basically give you a way to wait for some data to arrive and then run some code in response to the data arriving.