How to make a javascript callback function execute first? - javascript

I have the following code (that I have stripped and simplified):
function myOrder() {
setLocation(); // sets a value for global variable 'location'
// using GET
$.post('http://111.111.111.111/order', {
Item: Chair,
Price: 50,
Location: location;
})
}
The problem is that this function executes the post() code before the setLocation() callback function has finished.
I need the setLocation() function to finish first, so it can assign a global variable for 'location', which is then used as a value in the POST part.
Does anyone know if this can be done?
Thanks
EDIT: To clarify:
setLocation() is a GET request, that gets the current location via GPS and stores it under a global variable name 'location'.
So what I think is happening is that the myOrder() method POSTS before the callback function has finished its GET response.

Use async flow
function setLocation() {
return new Promise((resolve, reject) => {
// I do my stuff
// I finished
resolve();
});
}
function myOrder() {
setLocation().then(() => {
$.post('http://111.111.111.111/order', {
Item: Chair,
Price: 50,
Location: location;
})
})
}

As you did not provide the implementation of setLocation, I'll assume you use $.get or some similar jQuery function ($.ajax, $.getJSON, ...).
In that case, do not use the callback function of $.get, and do not store the response data a global variable, but return $.get(...) because it is a promise:
function setLocation() {
// Return the promise object returned by $.get, $.getJSON, $.ajax, ...
// instead of using the callback function, and don't store the result
// in a global variable
return $.get("myurl").then(function (data) {
// As you did not provide the code, this serves only as example. The
// way you extract the latitude and longitude from the response may be
// different. Adapt as needed:
data = JSON.parse(data);
var lat = data.result[0].lat;
var lng = data.result[0].lng;
// Return value will be the promised value: adapt the format as needed,
// This will become the location variable content in your other function
return { lat, lng };
});
}
function myOrder() {
// Use the `then` method on the jQuery promise, and return
// the result of the `then` method again, so you can chain
// on myOrder() as well...
return setLocation().then(function (location) {
// Return the `$.post` result:
return $.post('http://111.111.111.111/order', {
Item: Chair,
Price: 50,
Location: location;
});
});
}

If the setLocation function is an asynchronous function, you will need to change it to receive a callback function after it completes.
function myOrder() {
setLocation(function() {
$.post('http://111.111.111.111/order', {
Item: Chair,
Price: 50,
Location: location
});
});
}
Hope it helps.

Related

return success data in require.js with callback

i have a little understanding problem with my current code...
i create a new require js module.
define(['jquery','apiomat'],function($,Apiomat) {
var getLocation = function(data, callback) {
Apiomat.Localization.getLocalizations("locale like 'de'", {
data: data,
onOk: function (data) {
callback(data);
}
});
};
return {
getData: function(data, callback) {
getLocation(data, callback);
}
}
});
And if i try to access these function with:
var test = easy.getData();
app.logger("CALLBACK FROM COMMON: " + JSON.stringify(test));
I always get this error message.
TypeError: callback is not a function. (In 'callback(data)', 'callback' is undefined)
Im not really sure what i have done wrong.
getData takes two arguments. The second one is supposed to be a function, but you aren't passing any arguments at all, so it gets the value undefined.
It then calls getLocation with the same arguments and Apiomat.Localization.getLocalizations does its thing. Eventually getLocalizations (or another function called by it) calls getOK which attempts to call callback.
undefined is not a function, so you get the error message.
Additionally the getData function doesn't have a return statement so will be returning undefined. This means there is no point in assigning the return value to test.
You need to pass a function which does whatever you want to do:
function myCallback(test) {
app.logger("CALLBACK FROM COMMON: " + JSON.stringify(test));
}
… and pass arguments to getData.
easy.getData("some data", myCallback);

AngularJS Testing - Testing with $http and then() for AngularUI's typeahead

I have a problem with testing my function that I use with typeahead.js (https://github.com/angular-ui/bootstrap/blob/master/src/typeahead/typeahead.js). I usually know how to resolve a promise in tests, but not with the following function:
$scope.getSuggestion = function ( name, length ) {
return $http.get( 'api/autocomplete/?contains=' + name )
.then( function ( response ) {
return response.data.slice( 0, length || 7 );
});
};
My test looks like this:
describe('Suggestions', function () {
it('should be possible to get suggestions', function () {
$httpBackend.expectGET('api/autocomplete?title__contains=Foo').respond([
{ name: 'Foobar' },
{ name: 'Foobala' },
{ name: 'Foolooloo' }
]);
var suggestions = $scope.getSuggestion( 'Foo' );
$rootScope.$apply();
// Here should be a test.
console.log(suggestions);
})
});
But suggestion only is the promise object Object{then: function (callback, errback) { ... }}.
Where did I mess up!?
suggestions is a promise, it is not an actual value, you need to call then() to get the value out of it. That is
suggestions.then(function(data) {
// Here should be a test.
console.log(data);
});
Update:
Try this:
describe('Suggestions', function () {
it('should be possible to get suggestions', function () {
$httpBackend.expectGET('api/autocomplete?title__contains=Foo').respond([
{ name: 'Foobar' },
{ name: 'Foobala' },
{ name: 'Foolooloo' }
]);
var suggestions;
$scope.getSuggestion( 'Foo' ).then(function(data) {
suggestions = data;
});
$httpBackend.flush();
$rootScope.$apply(); // might be optional
// Here should be a test.
console.log(suggestions);
})
});
Regarding the $httpBackend service, in order to 'force' it to respond to the request of the $http service, it is sufficient to cal $httpBackEnd.flush();. I believe the $rootScope.$apply(); is redundant here.
Regarding the return value of the $scope.getSuggestion method, note that it will not return you the data from the server; it will return you a promise object which will be resolved once the request to the server is fulfilled.
Also note that promises can be chained, therefore the result of $http(...).then(....) in your method is still a promise.
Finally, the return statement in the callback passed as argument in then in your controller (return response.data.slice( 0, length || 7 );) will not be of much use; when the promise is resolved and this callback is invoked, you won't be able to get that return value.
You can of course provide code in the callback passed to then inside the controller method if there is something you need to do every time you call the getSuggestion method. If the 'client' of that method however needs the data the $http service returns, he will have to register his own callback to retrieve them.
So, to actually get the response data in your test (the 'client' of your controller method), you need to register the respective callbacks inside the test.
You can use the standard then method of the promise "interface" which expects you to pass two callbacks; the first will be called if the request succeeds (in your case if the $httpBackend is 'trained' to respond with a status in the 2XX range), the second one in case of an error. Alternatively you could use the AngularJS specific success and error methods since the $http service returns an HttpPromise.
You can check out this approach here: http://jsfiddle.net/yianisn/rgk97/
See also here and here.

Returning a value of callback function?

I'm having some trouble with function callbacks, specifically with the HTML5 geolocation API. I'm a little new to Js as well. The function:
function getInfo()
{
navigator.geolocation.getCurrentPosition(getPos);
}
takes a callback parameter of a function that gets the position passed into it.
function getPos(position)
{
PositionObj.lat = position.coords.latitude;
PositionObj.lon = position.coords.longitude;
}
I created a "location" object to store the coordinates, and I'm having trouble returning them.
var PositionObj =
{
lat:null,
lon:null,
returnLat: function()
{
return this.lat;
},
returnLon: function()
{
return this.lon;
}
};
function printStuff()
{
getInfo();
console.log(PositionObj.returnLat());
}
EDIT: Syntax mistakes fixed. When I call printStuff(), I still get "null."
To pass the callback, use just getPos instead of calling the function right away.
Use proper syntax to declare properties in PositionObj.
PositionObj = {
lat: null,
lon: null,
}
Your PositionObj literal is incorrect. the initial properties should be set with colons:
{ lat: '', lon: '' }
The callback should tell you that the request to get position information is asynchronous. This means that getInfo() does NOT set the values instantly, and therefore the following line cannot access the "new" values.
Anything that relies on the result of an asynchronous function MUST be within the callback itself.

jQuery deferred objects: unexpected behavior with .when()s, .then()s and .done()s

I have a bunch of nested functions returning deferred objects from ajax calls. Here's what my code looks like
function makeCalls() {
var ajaxDfd1 = $.ajax(...);
ajaxDfd1.then(function() {
// want to execute after first call
var ajaxDfd2 = $.ajax(...);
ajaxDfd2.done(function() {
// want to execute after second call
});
return ajaxDfd2;
});
return ajaxDfd1;
}
makeCalls().done(function() {
// stuff here was executed early
});
But my calls are not being executed in the order I intend them to be. Stuff inside makeCalls().done() seems to be called before ajaxDfd2 is actually done.
Choc,
You've made an incorrect assumption in your answer; namely that $.ajax() returns a "regular object".
This is not correct.
$.ajax() returns a jqXHR object, which is a superset of a Promise, ie. it has all of Promise's methods plus others which are specific to AJAX (such as .abort()).
If the object returned by $.ajax() was not Promise-compatible, then the code in your question would throw an error on trying to execute makeCalls().done(...).
The reason why your .done() function executes first it that the function returns the outer jqXHR.
Here's a couple of ways to achieve the behaviour you were expecting :
function makeCalls() {
var dfrd = new Deferred();
$.ajax(...).then(function() {
$.ajax(...).done(dfrd.resolve);
});
return dfrd.promise();
}
makeCalls().done(function(data) {
// ...
});
or
function makeCalls(fn) {
$.ajax(...).then(function() {
$.ajax(...).done(fn);
});
}
makeCalls(function(data) {
// ...
});
You could even try :
function makeCalls() {
return $.ajax(...).then(function() {
return $.ajax(...);
});
}
makeCalls().done(function() {
// ...
});
I knew I must be getting something wrong, and spent a really long time staring at this:
When should I use jQuery deferred's "then" method and when should I use the "pipe" method?
Later I realised that I can't do
var dfdObj = $.ajax(...);
dfdObj.then(...);
return dfdObj;
This doesn't actually return the deferred object. It returns an Ajax object, and since it is a regular object, it is automatically classified as resolved and thus the .done() function was being called early. Instead, I need to do this if I want to return a var
var dfdObj = $.ajax(...).then(...);
return dfdObj;
Your code works like this:
function makeCalls() {
var ajax1 = $.ajax(…);
var ajax1and2 = ajax1.then(makeCall2);
function makeCall2() {
// want to execute after first call
var ajax2 = $.ajax(…);
ajax2.done(after2);
function after2() {
// want to execute after second call
});
return ajax2;
});
return ajax1;
}
var ajax1 = makeCalls();
ajax1.done(function() {
// stuff here was executed early
});
ajax1and2 is the result of the then/pipe call you wanted. A callback on it will wait for the result of makeCall2 (i.e. the ajax2) which waits for ajax1. It would be equal to the after2 callback.
Yet, your makeCalls function returns the ajax1 object (you can see I assigned it to a global ajax1 variable as well) and both the makeCall2 and the done-callback will be executed after your first ajax has finished.
So, instead you need to return the ajax1and2 deferred:
function makeCalls() {
return $.ajax(…).then(function() {
return $.ajax(…).done(function() {
// want to execute after second call
});
});
}
makeCalls().done(function() {
// stuff here is now executed after second call as well
});

Javascript callback - how to return the result?

I am struggling to totally understand callbacks and i am stumbling at the final hurdle.
Within JS I am calling a function which then calls a PHP function using a dojo rpc Json Service. I have stepped through the function in firebug and the PHP is executing and returning me the correct response via the callback but I don’t know how to return the value to the initial JS variable that invoked the JS function? E.g.
JS Function 1
Function one(){
Var test = getPhp(number);
}
function getPhp(number)
{
this.serviceBroker = new dojo.rpc.JsonService(baseUrl + '/index/json-rpc/');
var result = serviceBroker.phpFunc(number);
result.addCallback(
function (response)
{
if (response.result == 'success')
{
return response.description;
//I am trying to pass this value back to the
//var test value in function one
}
}
);
}
Basically i now need to pass response.description back to my var test variable in function one.
Any help is appreciated
This is not possible, since the callback is run asynchronously. This means that the getPhp function returns before the callback is executed (this is the definition of a callback, and one of the reasons asynchronous programming is hard ;-) ).
What you want to do is create a new method that uses the test variable. You need to call this method when the callback is executed.
i.e.
function one(result) {
var test = result;
// Do anything you like
}
function getPhp(number, callback) {
this.serviceBroker = new dojo.rpc.JsonService(baseUrl + '/index/json-rpc/');
result.addCallback(
function (response)
{
if (response.result == 'success')
{
callback(response.description);
}
}
);
}
getPhp(number, function(result) { one(result); });
This last method creates an 'anonymous function' that is passed to the getPhp function. This function gets executed at the time the response arrives. This way you can pass data to the one(number) function after the data arrives.
The short answer is that you can't.
Do whatever you want to do with the data in the callback or functions you call from the callback. You can't return anything from it.
A much cleaner answer:
getPhp(number);
function one(data){
var test = data;
// do what you want
}
function getPhp(number)
{
this.serviceBroker = new dojo.rpc.JsonService(baseUrl + '/index/json-rpc/');
var result = serviceBroker.phpFunc(number);
result.addCallback(
function (response)
{
if (response.result == 'success')
{
one(response.description);
}
}
);
}

Categories