New to Javascript - Callback issue - javascript

I am new to Javascript programming coming from a Java and Objective C background. I am looking to learn a bit more about Javascript for hybrid mobile applications.
In doing so I am trying to do a login with a call back but I am having a little trouble understanding both the syntax and the way a callback works.
First up I am calling the following login function which just creates an ajax call to fetch some JSON at the minute for testing purposes:
testLogin.loginWithUsername ("test", loginCallback);
This works OK as I can see the 200 OK Status and the expected JSON in logging.
However the call back "loginCallBack" never gets called.
It is as follows:
loginCallback: {
success: function(id) {
alert ('success');
}
failure: function (id, error) {
alert ('failure');
}
}
First off the above gives me a syntax error when I try to run the code, at the success:function(id) line. I can change it and the failure function to = function(id) and it the code runs then but the callback is never called.
I am using a library for the login that states the call back required is an object that needs a success and failure function and the code above is the given example.
So first off I don't understand why the above syntax works in the sample code but gives me an error when I run it?
Secondly am I calling the callback correctly? Both the loginWithUsername call and the loginCallback function are in the same Login.js file.

Here is an example how callback works:
First thing: you need to create a new object containing your functions/methods. Properties and methods are listed comma seperated.
// Creating new object with callback functions
var loginCallback = {
success: function(id) {
alert ('success');
} , // Add a comma
failure: function (id, error) {
alert ('failure');
}
}
function loginWithUsername(username, callback) {
if (username === 'test') {
var successId = 1;
callback.success(successId);
} else {
var errorId, errorMsg;
errorId = 0;
errorMsg = 'Error';
callback.failure(errorId, errorMsg);
}
}
Now you can call the function:
loginWithUsername('test', loginCallback);
And the result should be 'success'.
Edit:
But you can do this without an object, by passing the function directly:
// Creating function
function showMessage(message) {
alert(message);
}
function loginWithUsername(username, callback) {
if (username === 'test') {
callback('success');
} else {
callback('failure');
}
}
// Pass function
loginWithUsername('test', showMessage); // Result: 'success'
loginWithUsername('abcd', showMessage); // Result: 'failure'

First off the above gives me a syntax error when I try to run the code, at the success:function(id) line.
Each property: value pair in an object literal must be separated with a comma.
}, /* Your comma is missing which is why you get a syntax error */
failure:
This:
loginCallback: {
is only acceptable if you want to define a property inside an object literal.
This:
testLogin.loginWithUsername ("test", loginCallback);
is using a variable.
You probably want:
var loginCallback = {
but it is hard to tell without more context.

Related

How to break some logic out of an async callback?

I'm trying to make an api call using the callback method in request, but I'm new to web development and I'm stuck on async at the moment. I've got the following working, but I want to break the logic out some. For example, here's what is working currently
const request = require('request');
class GetAllData{
constructor(){
this.loadData();
}
// Gets all data from api query that I'll need to build everything else I'll need
data(callback){
request({'url':`https://definitely/a/url.json`, 'json': true }, function (error, response, body) {
callback(body);
});
}
loadData(cmrUrl){
console.log("loadData");
this.data(function(result){ console.log(result.foo.bar)});
}
}
var moreData = new GetAllData();
This works, and I can do the two things I need, which are log some results, and make a small calculation with the results. However, if I want to break this logic out into other functions, I get some errors.
const request = require('request');
class GetAllData{
constructor(){
this.loadData();
// Member variables
this._subsetOne;
this._thingICalculated;
// Function call to print data outside of the async call.
this.printData(this._subsetOne, this._thingICalculated);
}
// Gets all data from api query that I'll need to build everything else I'll need
data(callback){
request({'url':`https://definitely/a/url.json`, 'json': true }, function (error, response, body) {
callback(body);
});
}
loadData(cmrUrl){
console.log("loadData");
// Set a class member variable
// ERROR: 'this' is undefined
this.data(function(result){ this._subsetOne = result.foo.bar)};
// Call a member function, which calculates something, and then sets a member variable.
this.calculateSomething = result;
console.log(result);
};
}
// Function which takes in result from async call, then calculates something.
set calculateSomething(result){
this._thingICalculated = result + 1;
}
printData(x, y){
console.log(x,y);
}
}
var moreData = new GetAllData();
From what I've been reading the issues I'm hitting are pretty common, but I'm still not understanding why this isn't working since the call is asyncronous, and I'm just trying to set a variable, or call a function. I'm assuming there's some way to ask the member variable setting and function call to await the completion of the async request?
Fix attempt one
const request = require('request');
class GetAllData{
constructor(){
this.loadData();
this._subset;
}
// Gets all data from api query that I'll need to build everything else I'll need
data(callback){
request({'url':`https://definitely.a.url/yep.json`, 'json': true }, function (error, response, body) {
callback(body);
});
}
loadData(cmrUrl){
console.log("loadData");
this.data(function(result){ this._subset = result
this.getSubset()}.bind(this));
}
getSubset(){
console.log(this._subset);
}
}
var moreData = new GetAllData();
Subset ends up being undefined.
In the constructor, you have to bind any member method that uses this to itself. So before calling loadData:
this.loadData = this.loadData.bind(this);
Also seems like this.data will get its own scope in loadData, which is why this returns undefined inside that function. Thus you have to bind this.data to this (your class instance) as well.
And do the same for any method that accesses this. The problem is classic JavaScript functions by default have an undefined scope. Arrow functions however automatically inherit the scope of the caller.

$.getJSON Callback Help - Not a Function

My context is as follows:
I have a variable 'qq' - I want to assign data to this variable from an API call, and once the data has been assigned, I then use this data in a paint/binding process.
The API call and assigning to var 'qq' works fine - but I cant seem to get my callback to work - I keep getting an error: callback() is not a function
This is what my code currently looks like:
var qq = [];
// binding function - to be run after data loader has completed
function sayIamDone() {
alert('I Am Done');
// do data bindings with qq
}
// load data from api into var qq
function dataLoader(inp,callback) {
let url = 'http://localhost:65232/api/layercolor/' + inp;
console.log(url);
$.getJSON(url,function(result) {
qq = eval('({' + result + '})');
//console.log(qq);
callback();
});
}
// call data loader
dataLoader('Prov', sayIamDone());
From the above I get the following error in my chrome console:
Uncaught TypeError: callback is not a function
I have tried the following - which seems to work:
dataLoader('Prov', function () { alert('I Am Done');});
but it is not ideal in my context, as I want to call dataLoader('Prov',XXX()) where XXX() could be a number of different functions using the newly loaded values in qq
Any suggestions as to where I am missing the boat please?
By running dataLoader('Prov', sayIamDone()); you are passing to dataLoader the result of sayIamDone function. You have to pass it without parenthesis in order to pass the function itself.
dataLoader('Prov', sayIamDone);
change:
dataLoader('Prov', sayIamDone());
to
dataLoader('Prov', sayIamDone);
because:
sayIamDone is function ,sayIamDone() is the result of function.Normally, sayIamDone() is undefined or return value in the sayIamDone;
want to use arguments?you can do this:
dataLoader('Prov', callFunc(n));
function callFunc(arg) {
return function() {
sayIamDone(arg);
};
}
or more arguments?you can do this by spread
dataLoader('Prov', callFunc(n));
function callFunc(...arg) {
return function() {
sayIamDone.apply(null, arg);
};
}

json after success return undefined

I am using JS with Angular came to the following problem.
I am getting data from a requestService, so a call the request function and then do something with the data in a 'success' function. Inside this function, everything is fine and I get all my results but as soon as a leave the success function, my results are undefined. I read some other questions/answers about similar problems, and tried other things. However I do not really know how to hand this and wanted to ask this explicitly with a code example:
function loadShips() {
var count = 0;
RequestService.getShips(nelat, swlat, nelong, swlong, timestamp)
.success(function(results) {
var groupedShips = results.aisData.aisGroupedByShipType;
_.each(groupedShips, function(groupedShip) {
_.each(groupedShip, function(ship) {
Markers['marker' + count] = createMarker(ship);
count++;
});
});
console.log(Markers, '#1')
return Markers
});
console.log(Markers, '#2');
return Markers;
}
So anyone could maybe tell me, why Markers at the print out of '#1' are defined and at '#2' are undefined.
Assuming the request is being done asynchronously, the call at #2 is happening before the request's success method is being called. This would explain why the object does not exist yet.
One solution would be to pass a call back method into the factory as a parameter, and then call that method after the request success has occurred. That would look something like this:
function loadShips(callBack) {
var count = 0;
RequestService.getShips(nelat, swlat, nelong, swlong, timestamp)
.success(function(results) {
var groupedShips = results.aisData.aisGroupedByShipType;
_.each(groupedShips, function(groupedShip) {
_.each(groupedShip, function(ship) {
Markers['marker' + count] = createMarker(ship);
count++;
});
});
console.log(Markers, "#1");
callBack(Markers);
});
}
Using this method looks like this:
function myCallback(markers){
console.log(markers, "#2");
//assign markers to something
}
loadShips(myCallback);
As Will P pointed out, with asynchronous functions, the inline code after them will execute first, because the success function is still waiting in the event queue.
in addition to that, Markers is being returned from inside a anonymous function which will not return it as the result of loadShips but will return it inside ajax wonderland, never to be seen.
what you will have to do is have a function that receives the data when it is ready and call that function with the data. I'm assuming things happen after the ships load, those things will have to be called after the anonymous function is done creating Markers.
function loadShips() {
var count = 0;
RequestService.getShips(nelat, swlat, nelong, swlong, timestamp)
.success(function(results) {
var groupedShips = results.aisData.aisGroupedByShipType;
_.each(groupedShips, function(groupedShip) {
_.each(groupedShip, function(ship) {
Markers['marker' + count] = createMarker(ship);
count++;
});
});
doFancyWonderfulThingsInTheOcean(Markers);
});
}

why is my exception not being caught?

I am writing a jQuery plugin that lets users add references to their article. Basically, when the enter key is pressed while the user is focused on the reference input, the script checks the URL through my AJAX script to make sure that it's a working URL. If it's valid, a button is added to a list of references that the user can see. It will also update a hidden input that contains a comma-separated list of URL's.
I am very new to the concept of JS exceptions... I am currently getting an error saying Uncaught [object Object]. The error happens where I throw the variable 'info'. Any ideas?
(function($){
$.fn.extend({
references : function(options) {
var defaults = {
sample_div : '#sample-ref',
remove_button : '#removereference',
update_div : '#references',
hidden_input : 'input[name="references"]',
saved_input : 'input[name="saved-refs"]',
ajax_url : 'ajax.php',
search_input : '#reference'
};
var options = $.extend(defaults, options);
var count = 0;
function addReferenceBlock(ref_title){
var replacements = {
ref_title : ref_title,
ref_url : ref_url
};
var block = $(options.sample_div).html();
$.each(replacements, function(index, value){
block.replace(new RegExp('{'+index+'}', 'g'), value);
});
$(options.update_div).append(block);
}
function checkReference(url){
var postData = 'reference='+url;
$.ajax(
{
dataType: "xml",
type: "POST",
data : postData,
cache: false,
url: options.ajax_url
})
.done(function(xml, textStatus, jqXHR){
if(textStatus === 'success'){
$(xml).find('success').each(function(){
console.log('checking '+url+'...');
var info = $(this).find('pagetitle');
throw info;
});
$(xml).find('error').each(function(){
throw false;
console.log($(this).find('message').text());
});
} else {
console.log(jqXHR);
}
});
}
function init(element, options){
$(options.search_input).enterKey(function(e){
try {
checkReference($(options.search_input).val());
} catch($status){
if($status !== false){
addReferenceBlock($status);
updateReferenceInput($(options.search_input).val());
} else {
alert($status);
}
}
e.preventDefault();
});
}
return $(this).each(function(){ init(this, options); });
}
});
})(jQuery);
Your try block calls the checkReference function. Your checkReference function calls done. done does not call the anonymous function which throws your error; it sets up an event handler so it can be called by the system later. Thus, your stack trace is not what you think it is.
EDIT
Why does "done" not call the code inside of it?
Because if it did, it would not be asynchronous. Let's mock this with setTimeout rather than AJAX, same principles apply:
function foo(how) {
throw("In foo " + how + " (look at the stack trace by clicking on the triangle)");
}
function callFooAsynchronously() {
console.log("calling foo asynchronously");
setTimeout(function() {
foo("asynchronously");
}, 1000);
console.log("finished calling foo asynchronously");
}
function callFooSynchronously() {
console.log("calling foo synchronously");
foo("synchronously");
console.log("finished calling foo synchronously");
}
function main() {
callFooAsynchronously();
callFooSynchronously();
}
main();
The output is as follows:
calling foo asynchronously js:18
finished calling foo asynchronously js:22
calling foo synchronously js:26
Uncaught In foo synchronously (look at the stack trace by clicking on the triangle) js:14
foo js:14
callFooSynchronously js:27
main js:34
(anonymous function) js:37
Uncaught In foo asynchronously (look at the stack trace by clicking on the triangle) js:14
foo js:14
(anonymous function)
The synchronous call will start, then throw an exception. Due to the exception, "finished calling foo synchronously" is never displayed. The stack trace shows call from the snippet execution environment, calling to main, which calls callFooSynchronously, which, ultimately, calls foo.
The asynchronous call will display the starting message, attach a timeout handler, then display the finished message, then exit. This concludes callFooAsynchronously. One second later, the browser will remember there is something it needs to do, and this is reflected in the stack trace: the anonymous function passed to setTimeout is run, which in turn runs foo. Note how main and callFooAsynchronously are not a part of the stack trace: they have set the alarm, then left the building. callFooAsynchronously, despite its name, never calls foo, and neither does setTimeout.
The browser calls the anonymous function in setTimeout directly, just as it calls directly the onreadystatechange function on an XMLHttpRequest (the function that ultimately calls the function you passed to done), which is attached, but not called, by jQuery.ajax.
If done called your function, it would be executed immediately after you made the ajax call, and not when the response arrives, because that is when done gets executed.
In JS, you can throw errors using error-strings like: throw new Error('You did something wrong')
So in your case, maybe you can try: throw new Error(info.text()) which will fetch the text inside the .pagetitle element.
Is this what you want?

jQuery Ajax How do callbacks work?

Hello fellow programmers! I just started an additional programming project and swore to god my code will bo SO much cleaner and easily upgradeable than it has been before.
Then I stumbled upon my "arch enemy" the jQuery AJAX returning. Last time I wanted to return something from an AJAX call I had to bend over and just make the call synchronous. That made things sticky and ugly and I hope that this time I will find something better.
So I have been googling/searching stackoverflow for a while now, and just don't understand this solution many ppl has gotten which is called callback function. Could someone give me an example on how I could exploit these callback functions in order to return my login statuses:
function doLogin(username, password) {
$.ajax({
url: 'jose.php?do=login&user='+username+'&pass='+password,
dataType: 'json',
success: function(data) {
if(data.success==1) {
return('1');
} else {
return('2');
}
$('#spinner').hide();
},
statusCode: {
403:function() {
LogStatus('Slavefile error: Forbidden. Aborting.');
$('#spinner').hide();
return (3);
},
404:function() {
LogStatus('Slavefile was not found. Aborting.');
$('#spinner').hide();
return (3);
},
500:function() {
LogStatus('Slavefile error: Internal server error. Aborting.');
$('#spinner').hide();
return (3);
},
501:function() {
LogStatus('Slavefile error: Not implemented. Aborting.');
$('#spinner').hide();
return (3);
}
},
async: true
});
}
So as you probably know, you cannot use return the way I have done from inside an AJAX call. You should instead use callback functions which I have no idea of how to use.
I'd be VERY greatful if someone could write me this code using callback functions and explaining to me just HOW they WORK.
EDIT:
I REALLY need to return stuff, not use it right away. This function is being called from within another function and should be able to be called from different places without being rewritten even slightly.
/EDIT
Sincerly,
Akke
Web Developer at Oy Aimo Latvala Ab
There are three parts to the basic "I need an asynchronous callback" pattern:
Give the function a callback function parameter.
Call the callback function instead of returning a value.
Instead of calling the function and doing something with its return value, the return value will be passed to your callback function as a parameter.
Suppose your synchronous mind wants to do this:
function doLogin(username, password) {
// ...
return something;
}
switch(doLogin(u, p)) {
case '1':
//...
break;
case '2':
//...
break;
//...
}
but doLogin has to make an asynchronous call to a remote server. You'd just need to rearrange things a little bit like this:
function doLogin(username, password, callback) {
return $.ajax({
// ...
success: function(data) {
if(data.success == 1)
callback('1');
else
callback('2');
},
//...
});
}
var jqxhr = doLogin(u, p, function(statusCode) {
switch(statusCode)) {
case '1':
//...
break;
case '2':
//...
break;
//...
}
});
The jqxhr allows you to reference the AJAX connection before it returns, you'd use it if you needed to cancel the call, attach extra handlers, etc.
A callback is simply a function that runs when certain conditions are met. In this case, it is when ajax has a "success".
You are already using a callback, but you don't recognize it. success: function(data) {} is a callback, but it's just what's called an anonymous function. It has no name or reference, but it still runs. If you want to change this anonymous function to a named function, it is really simple: take the code in the anonymous function, and put it in a named one, and then just call the named one:
[...]success: function(data) {
if(data.success==1) {
return('1');
} else {
return('2');
}
$('#spinner').hide();
}, [...]
should change to:
[...]success: function(){ callbackThingy(data) }, [...]
And now just create the callbackThingy function:
function callbackThingy(data){
if(data.success==1) {
someOtherFunction('1');
} else {
someOtherFunction('2');
}
$('#spinner').hide();
}
Note that the "return" value does nothing. It just stops the callback function, whether you are in an anonymous function or a named one. So you would also have to write a second function called someOtherFunction:
function someOtherFunction(inValue){
if(inValue=='1') {
// do something.
} else if(inValue=='2') {
// do something else.
}
}
The above example is if you have to pass parameters. If you do not need to pass parameters, the setup is simpler:
[...]success: callbackThingy, [...]
function callbackThingy(){
// do something here.
}
From the edit in your original post, I can see that you just need to store a (more) global variable. Try this:
// in the global scope , create this variable:
// (or -- at least -- in the scope available to both this ajax call
// and where you are going to use it)
var valHolder = -1;
// then edit your ajax call like this:
[...]
success: function(data) {
if(data.success==1) {
valHolder = 1;
} else {
valHolder = 2;
}
$('#spinner').hide();
},
[...]
Now you can verify 3 things:
valHolder = -1 means that the ajax call has not yet returned successfully
valHolder = 1 means data.success = 1
valHolder = 2 means data.success != 1.
Another option is to store the variable in an HTML attribute of some element.
Finally, you should probably look at jquery.data for the most jquery way of managing stored data.
Does this help?
Just as a small point of interest, you don't have to include
async : true;
as part of your $.ajax options. The default setting for async is already "true".
Sorry to post this as a response, but until I have 50 rep I can't make a simple comment. (Feel free to help me out with that! ^_^ )

Categories

Resources