How does javascript function call work? - javascript

I have this piece of code below:
It makes a GET call to an URL, gets some object, and appends an image to an HTML tag.
function getDataFromApi(searchTerm, callback) {
const URL1 = `some url`;
const design = {
url: URL1,
data: {
"dog breed name": searchTerm
},
type: 'GET',
success: callback
};
$.ajax(design);
}
function displaySearchData(data) {
const allResultsLength = data.message.length;
const ranNum = Math.floor(Math.random() * allResultsLength);
const dogResults = data.message[ranNum];
$(`.js-search-results`).html(`<img src = ${dogResults}>`);
}
function watchSubmit() {
$('.js-search-form').submit(event => {
event.preventDefault();
let queryTarget = $(event.currentTarget).find('.js-query');
let query = queryTarget.val();
queryTarget.val("");
getDataFromApi(query, displaySearchData);
});
}
$(watchSubmit);
I get the getDataFromApi and watchSubmit but getDataFromApi(query, displaySearchData); isn't intuitive to me at all.
I've been writing Java, and it doesn't make sense to me how displaySearchData is getting called without the parameter - it seems that line should be getDataFromApi(query, displaySearchData(data));.
Can someone please explain how this is getting compiled & executed (basically how this is a legitimate syntax) in javascript?

Somewhere in the good'ol jquery, there lies this piece of code:
$.ajax = function(config){
...
// get the response from XHR request,
// and save it in, say, 'response'
...
// now check, if the response is OK 200
// and if so, execute next line
// which is basically - calling your displaySearchData method
config.success(response);
...
}
now, config is your design object, which has a property success which carries the reference to your displaySearchData method.
The data argument of method displaySearchData will now carry the reference to variable response passed in the method invocation config.success(response).
EDIT: the argument callback also carries forward the reference of the method displaySearchData to getDataFromApi
Concept to be noted:
functions can be passed in Javascript as arguments to another function, in which case we only need the referring variable to be passed as argument. Invocation parentheses () are not required.
function A(data){...};
function b(referenceToFunctionA){
...
referenceToFunctionA(someData);
...
};
// correct
b(A);
// wrong, because adding () after any function reference variable
// invokes the method immediately.
// in this particular case the returned value of the method A
// is passed as argument instead of the reference to method A itself.
b(A());
Welcome to JavaScript My Friend. Get ready to experience more magical weirdness as you continue to work on JS. Good luck.

What you need to look at is in the function getDataFromApi().
In that function, you have a "callback" parameter. This parameter is later added into $.ajax. This is a jQuery function that will provide some callback when a certain condition is matched (like before sending a request, when the response has been received,...). This $.ajax callback provide you with 3 parameters, one of them is data (which are being used, textStatus, and jqXHR. Usually, you only need to pay attention to the data since it contains the response from where you are requesting data.
So when the $.ajax success, the "callback" function will be called, which is the displaySearchData. And since $.ajax callback provides you with the data parameter, you can add them to the parameters of displaySearchData. Do note that you can add the extra 2 provided parameters if needed.
You can have a look at that function here: jQuery Ajax

Related

Passing callback functions as arguments in vanilla JS ajax

I am working on an ajax function to retrieve info from the database. I set up the API and the ajax function does retrieve proper values, but the callback function I passed as an argument won't work on onreadystatechange.
Simplified Code below
function serializeArgs(args) {
//Serialize Arguments
}
function callback(a) { //The function to be called as callback
//Process the response and add contents to the page
}
function getListData(callback) {
var ajaxOptions = {
action: "get_data",
}
var request = new XMLHttpRequest();
request.open("POST", apiurl, true);
request.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded; charset=utf-8');
request.setRequestHeader("Accept-language", "en-US");
request.onreadystatechange = function(event) {
if (this.readyState == 4 && this.status == 200) {
callback(this.response);
}
};
request.send(serializeArgs(ajaxOptions));
}
When I run the function, I get the error "TypeError: callback is not a function"
I have been struggling with this all day, but since I am not that experienced with ajax, I could not figure out why this is happening, and how to solve it. I have a hunch that it has something to do with the asynchronous nature of the ajax, but I'm not sure how I can work around this.
Short reference for this error:
You usually get this error when the following happens:
When a function call is made on a property of that simply isn’t a function.
When a function call is made on an object type that doesn’t contain that function or method.
When a function call is made on a built-in method that expects a callback function argument to be provided, but no such function exists.
I noticed the following in your ajax function:
Your parameter is named callback, just like your function callback. What your ajax function is trying to do is use the parameter callback as a function (not sure if you pass the function callback in as a parameter, if you do, then it's alright, but since I cannot see where you are calling getListData function, I can only guess you are calling it without passing the function callback as an argument).
//Your function callback is trying to access this parameter.
//So unless your actual function callback is being passed in as an argument,
//it's most likely trying to access your parameter even though it is NOT a function or it isn't even receiving a function as parameter
function getListData(callback)
function callback(a)
However if you are trying to simply access it as a function (and not as a callback) i'd recommend changing the parameter name or function name callback.
So to show an example:
First way of doing what you are trying to achieve (callback way).
Call your function this way:
getListData(callback)
Or do the following:
function getListData(changedParamName) {
//this way you can now call your callback function and pass this.response to it
}
That should do the trick.
Where do you call the function getListData?
My guess is, that you call it like getListData() without passing the callback function.
if you call it like getListData(callback) it should work. Hard to say what happens without the full code example.
just for testing you also can change the line
function getListData(callback) {
to
function getListData() {
just to see if it works.
In the second szenario, you don't pass the callback, so when you call the callback function, Javascript will look for it in the parent scope.

Changing the value of a property of an object inside a function in java script doesn't work as it should

I have two modules and in the first one I declare an object because I know that primitives are passed by value in java script and objects by reference.I want to get the response status from a request and I am passing the object as a reference so I will be able to modify its property.The problem is that it doesn't do anything.In the end the value would be the same.
//this code is in a different module from the other one
var variableToBeChanged = { something : "Initial value" };
anotherModule.changeValue(variableToBeChanged);
alert(variableToBeChanged.something);
//and in the other module I have a $.ajax and I want to get the response status(ex. 200)
//the code is sth like this:
function AnotherModule(ajax){
function changeValue(variableToBeChanged){
...
...
...
$.ajax({
...
...
...
success: function(data,xhr){
variableTobechanged.something = xhr.status;
}
});
}
}
In the end it will display: "Initial value" instead of 200 or anything else.
What am I doing wrong here?
The ajax call is asynchronous and therefore the alert gets called before the variable is modified. You can use promise in ES6 like this to make sure it is executed after ajax call completes.
new Promise((resolve) =>{
anotherModule.changeValue(variableToBeChanged);
resolve();
}).then((res) =>{
alert(variableToBeChanged.something);
}).catch((error) =>{
alert(error);
});
In javascript copy of reference to object is passed.
This means that any changes made to the object will be visible to you after the function is done executing.
Since javascript is asynchronous , alert(variableToBeChanged.something) this line gets executed before your function returns . Therefore you see old value . You have to use callbacks or promise to work synchronously.
Please refer to this question javascript pass object as reference .It explains this concept beautifully.

Javascript returns result before all parameters evaluated?

I found the following example in ngResource documentation:
var cards = CreditCard.query(function() {
// GET: /user/123/card
// server returns: [ {id:456, number:'1234', name:'Smith'} ];
var card = cards[0];
// each item is an instance of CreditCard
expect(card instanceof CreditCard).toEqual(true);
card.name = "J. Smith";
// non GET methods are mapped onto the instances
card.$save();
// POST: /user/123/card/456 {id:456, number:'1234', name:'J. Smith'}
// server returns: {id:456, number:'1234', name: 'J. Smith'};
// our custom method is mapped as well.
card.$charge({amount:9.99});
// POST: /user/123/card/456?amount=9.99&charge=true {id:456, number:'1234', name:'J. Smith'}
});
As I can understand, the second parameter of function query() is a function, which evaluated on success result of resource query. But simultaneously, this function takes the variable cards which is assigned from result of function query().
I can't understand, if this is normal to Javascript, since every async operation executes single thread?
Or special efforts were taken by creators of AngularJS in order to have function paramater executed after it's result returned?
How would I write my own function
function myfunction(argument, runbefore, runafter) {
runbefore();
POSTPONE runafter();
return Math.sin(argument);
}
which would execute 2nd parameter before itself and 3rd parameter -- after itself?
If I understand right, you are asking how it is possible for the callback function to be called after the return statement. One way that this is possible is through builtin functions that call another function at a later time. Take this code for example:
function doItLater(arg1, callbackFn) {
setTimeout(1000, callbackFn);
return arg1;
}
This will return the same argument that it was passed, and the callback function will be called later (about 1 second after the function has already returned). There are other ways a callback function can be delayed. For example, with an XMLHttpRequest, a callback function can be called after an HTTP response has been received. You can also connect to user events, so that a function will be called when the user does something specific.
If you want a little clarification on how things like setTimeout work in a single-threaded environment, I would suggest reading this article by John Resig.

Run code after black box AJAX request returns

I have a function which makes an AJAX request to a server and returns relevant information after it completes.
I have another function which manipulates some variables in its namespace based on the returned information.
Currently, I am appending a 'callback' argument to the first function, which is called when the request completes. This, however, blurs the purpose of the first function - instead of being a 'getInfo' function, it's become a 'getInfoAndDo' function.
Ideally, I'd like to call the second function (a 'do' function, which calls the first function, a 'get' function) and does its thing.
I have looked around and found jQuery methods such as .ajaxStop and .ajaxComplete, but they seem to only to work when bound to DOM elements. Is there any way to do this entirely in javascript?
e.g.
function _getEventAttendance(uid, callback) {
var attendQuery = FB.Data.query('SELECT eid,rsvp_status,start_time FROM event_member WHERE uid = {0}', uid);
FB.Data.waitOn( [attendQuery],
function (args){
callback(args[0]);
}
);
}
function logAttendance(attendance){
console.log(attendance);
}
Currently, I am doing:
_getEventAttendance(123456789, logAttendance);
which seems ridiculous to me.
Is there a way to write the code such that I can change the code snippet inside _getEventAttendance / remove the callback argument:
FB.Data.waitOn( [attendQuery],
function (args){
return args[0];
}
);
and then make calls that are equivalently as simple as :
logAttendance.ajaxComplete(_getEventAttendance(123456789));
(I'm just making up the syntax for this, I have no idea how it's supposed to be written.)
$.when(<AJAX Request>).then(function(response){...});
Optionally use $.pipe() to filter response first.

Explaining jQuery AJAX Success Method

Im trying to use this jQuery script and this is confusing me:
function CallService()
{
$.ajax({
type : varType, //GET or POST or PUT or DELETE verb
url : varUrl, // Location of the service
data : varData, //Data sent to server
contentType : varContentType, // content type sent to server
dataType : varDataType, //Expected data format from server
processdata : varProcessData, //True or False
success : function(msg) {//On Successfull service call
ServiceSucceeded(msg);
},
error: ServiceFailed// When Service call fails
});
}
The bit im confused about is the sucess object. The jQuery documentation says:
success(data, textStatus, jqXHR)Function, Array
A function to be called if the request succeeds. The function gets passed three arguments: The data returned from the server, formatted according to the dataType parameter; a string describing the status; and the jqXHR (in jQuery 1.4.x, XMLHttpRequest) object. As of jQuery 1.5, the success setting can accept an array of functions. Each function will be called in turn. This is an Ajax Event.
But this method signature looks nothing like the:
success : function(msg) {//On Successfull service call
ServiceSucceeded(msg);
}
Object that we seem to be passing in.
Questions:
1) What does function(msg){ServiceSucceeded(msg)} mean?
2) What is 'msg' in this context?
3) How on earth am I meant to know how to structure the method sugnature for sucess?
Perfectly reasonable question. :-) In JavaScript, you don't necessarily have to call a function with as many args as it defines, and you don't have to define as many args as you may get called with. Which can be confusing if you're used to more constrained environments. :-)
Answering specifics:
1) What does function(msg){ServiceSucceeded(msg)} mean?
It defines a function (an anonymous one) that accepts one named argument (msg) and calls ServiceSucceded passing in that arg. jQuery will call the function with the three arguments defined by the jQuery documentation for the success function, but this particular success function is only using the first of those (data). More about named functions vs. anonymous functions here.
2) What is 'msg' in this context?
The first argument to the function. jQuery's docs call this first argument data, but you can call it whatever you like.
3) How on earth am I meant to know how to structure the method sugnature for sucess?
You did the right thing, it's in the jQuery documentation.
This thing about function arguments can be confusing, so let's do some examples:
function foo(arg) {
alert(arg);
}
That's perfectly clear, I'm defining a function called foo that takes a single named argument, arg. And thus:
foo("Hi there"); // alerts "Hi there"
But I can also do this:
foo(); // alerts "undefined"
There, I didn't give any arguments for foo, and so within foo, arg is undefined.
I can also do this:
foo("Hi there", "again"); // alerts "Hi there"
I'm calling foo with two arguments, but foo only makes use of one of them.
I could define foo to use as many arguments as you pass in:
function foo() {
var index;
for (index = 0; index < arguments.length; ++index) {
alert(arguments[index]);
}
}
arguments is an automatic thing all functions have, which is a pseudo-array (it's not really an Array) of the actual arguments the function was called with. And so:
foo("Hi there", "again"); // alerts "Hi there", and then alerts "again"
You can even mix named and unnamed arguments:
function foo(arg) {
var index;
alert(arg);
for (index = 1; index < arguments.length; ++index) {
alert("[" + arguments[index] + "]");
}
}
So now
foo("Hi there", "again"); // alerts "Hi there" and then alerts "[again]"
Note the [] around the second alert, because I started looping with index 1 rather than zero.
arguments and named args are connected:
function foo(arg) {
alert("arg = " + arg);
alert("arguments[0] = " + arguments[0]);
arg = "Updated";
alert("arg = " + arg);
alert("arguments[0] = " + arguments[0]);
}
If I do foo("Hi");, that shows these alerts:
arg = Hi
arguments[0] = Hi
arg = Updated
arguments[0] = Updated
(It goes the other way, too, if you update arguments[0].)
The function is passed 3 parameters: data, status, and the jqXHR object. data is what is returned from the AJAX call, status is the HTTP status code (I think), and jqXHR is a jQuery wrapped XHR object.
In this script, they only care about the data parameter, and not the other two.
So using success: function(msg), they only get the data parameter. The other two are sent, but ignored.
ServiceSucceeded is just a function that is being called with the data parameter sent to it.
success: ServiceSucceeded could have also worked here.
It means the success handler invokes ServiceSucceeded with the response of the request.
msg contains the response from the request. msg maps to data in the jQuery documentation.
You need to look into the jQuery documentation for finding the signature.
This is an anonymous function.
It's like a regular function, but without a name.
msg is the function's first parameter.
By reading the documentation.
jquery Ajax is a way for you to communicate with the server (PHP, ASP, whatever). Let's assume you use PHP. the function "callService()" send a request to "varUrl" (validation.php, i.e) and get (or POST -> varType) the content (varContentType -> valdation.php?id=1231&whatever=soemthing). The purpose of this is to get some server side data without reloading the page. If you want the validation.php to echo some html, then the dataType in the Ajax function must be "html". See jquery.com for more info on dataType.
The success parameter is a function handler for the server response. Success is called if you get a response from the server corresponding to the dataType you asked (html, json, text, whatever). In that perticular case, if the server respond correctly, the function "ServiceSucceeded" is called with the attribute "msg" which is the server response you asked for.
1) That function is called if the AJAX request is successful i.e. a success status code is returned by the server being contacted.
2) I would assume that 'msg' is the data returned from the server. The other two arguments are not supplied and therefore not used.
3) Use the Jquery documentation, and fiddle around until you get what you want.
Even though the success function is defined as taking three parameters (as per the documentation you quoted), those three parameters are not mandatory - Javascript is very forgiving about this sort of thing; if you miss a parameter from a function call, it simply gets set to underfined, so as long as you don't try to use it, JS won't throw any errors.
The code you've provided only gives one parameter - msg - but in JS, this is perfectly valid; it just means that msg will be the data parameter defined in the docs, and textStatus and jqXHR will be undefined.
This is fine, as long as in your success function you don't actually want to use either of those parameters. If you want to use them, then pass them, but if not, it's fine to drop them. You're writing the success function, so you get to decide which of the three parameters to use.

Categories