AJAX + jQuery Deferred: execution sequence - ajax

Task: get data from server with $.post, process them by method .success(), after that call some function.
var t;
$.when($.post("get_json.php", function(res) {
t = res;
}, 'json')).done(function() {
console.log(t);
});
Do I understand correctly that the Deferred method .done() is executed after .success is done (ie t = res)?
But why "console.log(t)" shows "undefined"?
Is .done() fires after request, but before .success()?

Passing a "success" callback to $.post() is an alternative to the (preferred) chaining of .done(...). Do one or the other, not both, then you don't need to worry about the execution order.
Also, unless you have a decent caching strategy for async data, you shouldn't be setting t as an outer var.
$.post("get_json.php", ...).done(function(t) {
console.log(t);
//do awesome things with t here
});
Caching would be something like this :
var asyncCache = {};
...
function get_t() {
return (asyncCache.t) ? $.when(asyncCache.t) : $.post("get_json.php", ...).done(function(t) {
asyncCache.t = t;
});
}
...
get_t().done(function(t) {
console.log(t);
//do awesome things with t here
}

$.when() is ONLY needed when you have multiple promises and you want to wait for all of them to complete. You simply don't need it at all for your single ajax call. You can just do it like this:
$.post("get_json.php").done(function(t) {
// use the results of the ajax call here
console.log(t);
});
In addition, your code example was using BOTH a success callback function AND a .done() handler. Pick one of the other, not both as they are different ways of getting a callback when the ajax call is done. I'd suggest the promise implementation above because it's more flexible. But, you could also use just the success handler:
$.post("get_json.php", function(t) {
// use the results of the ajax call here
console.log(t);
}, 'json');
Note, when you have an asynchronous operation like this, you need to consume the results of the ajax call in the success callback (or the promise callback) or call some function from there and pass it the data. You do not want to put the data into a global variable or a variable in a higher scope because other code will simply have no way of knowing when the data is ready and when it is not. Put your action in the callback.
If you have more than one ajax call that you want to wait for, then you can do it like this:
$.when($.post("get_json.php"), $.post("get_json2.php")).done(function(r1, r2) {
// use the results of the ajax call here
console.log(r1[0]); // results from first ajax call
console.log(r2[0]); // results from second ajax call
});

Related

How to avoid loop in Ajax call

I have function getData() which make an ajax call and retrive JSON data. On success i call another function which is marquee() . inside marquee on finish event i call getData() again, But each time getData() when get called, it increases it's request to mentioned file data.php, For example on first call it call once, Second call it request twice, and then twice become 4times,8times and more and more, how to avoid this?!
function getData()
{
$.get('data.php).done(function(response)
{
var data = JSON.parse(response);
if(data.Direction == "left")
{
$(".marquee").html("<span data-direction='"+data.Direction+"'>"+data.Message+"</span>");
}else if(data.Direction == "right"){
$(".marquee").html("<span data- direction='"+data.Direction+"'>"+data.Message+"</span>");
}
});
}
function marquee()
{
$(".marquee").marquee({duration : 10000}).bind("finished",function()
{
getData();
});
}
I hope i was clear... Appreciate each answer.
Every time you are calling marquee function, you are basically binding an event finished on to it. On multiple such function calls, you will have duplicate events. In your code setup, you need to unbind the function before binding it. Something like
$(".marquee").marquee({duration : 10000}).unbind("finished",getData).bind("finished",getData)
Ideally, you should bind only once so you do not have to unbind it again and again.

Jasmine use it to over ride and test a method that uses aiax

I have a JS file that contains many methods, ajax caller is constant.
function ajaxCaller(data,url,callback) { //standard jquery ajax call here }
function method1(){
// ... does work ... then
ajaxCaller(data,url,function(){ // changes the dom } );
}
How can I use jasmine to over ride the ajaxCaller method that is being called by nearly every method in my js file such that I can test that the DOM is getting changed?
Probably you have something like the code below, right?
function ajaxCaller(data, url, callback) {
$.ajax(url, { data: data, success: callback });
}
In this case you can mock the jQuery ajax method so that instead of real AJAX request the function you provide will be called. It is possible to define the response you want to be "returned from the server". Jasmine andCallFake function will do it for you:
it ('when some response returned from the server, something happens', function() {
var response = {}; // ... define the response you need
spyOn($, 'ajax').andCallFake(function(url, options) {
options.success(response);
});
method1();
// and assert that something really happens ...
});

How can I handle async calls when validating with Backbone.js (uniqueness specifically)

This is relevant for either client or server side apps using backbone. I am attempting to create a validation function with uniqueness checks to MongoDB or some REST call (depending on environment). Both of these calls are async by nature; however, I think I actually need to make it block here for validation purposes. If I don't return anything the validate function will assume validation passed.
My code currently looks like this on the server side:
isUnique: function (key) {
var dfdFindOne = this.findOne({key: this.get(key)}),
dfd = new Deferred();
dfdFindOne.done(function (err, result) {
console.log(result);
dfd.resolve(true);
});
return dfd;
};
... some stuff here....
I feel like I can do some sort of wait till done functionality here before I return... perhaps not though. I wish backbone provided a callback function or something or accepted some sort of deferred type thing.
validate: function() {
var result = undefined;
if(!this.isUnique(key).done(function(){
result = "AHHH not unique!";
});
return result;
}
A possible solution might be to force mongodb's native node client to call things synchronously. I think I can do the same with rest calls... This is probably a bad solution though.
You could call the ajax request and set async:false in this way the return will have value. However to use async:false is evil because could appear as the browser is locked. For server side maybe there are not always workarounds for set async: false
My recommendation is to use your own validation flow instead of Backbone.validate flow, because the validation flow of Backbone was made thinking for synchronous validations only. You could try something like this:
//Code in your Model
isUnique: function (callback) {
var dfdFindOne = this.findOne({key: this.get(key)});
dfdFindOne.done(function (err, result) {
console.log(result);
callback(result);
});
};
validate: function(callback) {
this.isUnique(callback);
}
//trying to validate before save
model.validate(function(result){
if( result == 'whatexpected'){
model.save();
}
});

Is it possible to set a Knockout dependent observable with AJAX POST

Using Knockout 2.0 and MVC3 Razor forms, I am not able to make a dependent observable work when I introduce an ajax method. I have set up a set of observables that are a part of a calculation, and when I return the product of those observables, I am able to set a SPAN tag with the correct result. However, when I try to use the ajax method to handle those observables and return a result, I get unpredictable behavior. First, it appears the ajax POST does not pick up one of the observables when the INPUT fields are updated (var b POSTs to the action method as 0, but then is eventually updated), and then it seems that I am not able to set the result even when it evaluates correctly. It appears there is timing issue with either the observables or the ajax call. Although simply keeping performing the calculation in javascript works fine, my intent is to call ajax methods for more complicated logic. I have removed the call to ko.applybindings from doc.ready(), and also moved the SCRIPT methods to the bottom of the page- this was the only way I found to make this partly functional. My viewModel is set up as follows:
var viewModel = {
a: ko.observable(0),
b: ko.observable(1),
c: ko.observable(2),
// commented this out, since
// the dependent observable will handle this
// d: ko.observable(0)
};
In my dependent observable:
viewModel.d = ko.dependentObservable(function () {
var theResult = 0;
$('.theLabel').css("visibility", "visible");
theResult=viewModel.a() * viewModel.b() * viewModel.c();
// if we return here we get a valid result
return (theResult);
// prefer to call ajax method
// first check to ensure one variable is set
if (viewModel.a() > 0) {
$.ajax("/myCalculation/getResult", {
data: ko.toJSON(viewModel),
type: "post",
context: viewModel,
contentType: "application/json",
success: function (result) {
// can't set visibility here
$('.theLabel').css("visibility", "visible");
// the POST does not pick up some observables, or
// does not the set dependent observable at all
return result;
}
});
}
});
There is quite a bit wrong with the function you have setup.
1.) You are returning from your function before making your AJAX call. The code after your return statement will never execute.
2.) Even if you omit the first return statement, your AJAX call is Asynchronous... which means it will execute in the background and return control to the outer scope immediately. Since you don't have a return statement, then you are going to return undefined.
3.) The comment in your success callback suggests you are expecting the return statement to propagate all the way up to your computed observable. THAT return statement is only scoped to the callback, and not the outer observable. The return value will be used by jQuery, and your observable will long since have returned.
If you want an observable to call an AJAX function you need a seperate value to store the results of the asynchronous call.
Here is a simplified example:
var viewModel = function(){
var $this = this;
$this.a = ko.observable();
$this.b = ko.observable();
$this.results = ko.observable();
//No need to assign this computed observable to a variable
// because the results will be stored in '$this.results'
// we just need this to handle the automatic updates
ko.computed(function(){
var data = {
a: $this.a(),
b: $this.b()
};
$.post("/do/some/stuff", data, function(results){
$this.results(results);
});
});
};

Make jQuery ajax calls in order

I want to make a stack of Ajax calls in this way: call(n) starts after call(n-1) finished...
I cannot use async:false for many reasons:
some requests maybe jsonp (the most relevant)
I have other ajax requests that may work meanwhile..
The browser got blocked
I cannot chain my requests this way:
$.post('server.php', {param:'param1'}, function(data){
//process data
$.post('server.php', {param:'param2'}, function(data){
//process data
});
});
Because the number and params of the requests are dynamically created from user input.
A small example that illustrates my problem.
You will see that the server response order is random, what I want to achieve is to have it in order
Response to arg1
Response to arg2
Response to arg3
Response to arg4
Response to arg5
Response to arg6
Any help would be very appreciated, thanks.
Ok, jQuery Ajax returns a Deferred Object, this can help you achieve this.
Here is how to do it:
var args = ['arg1','arg2','arg3','arg4','arg5','arg6'];
deferredPost(0, 5);
function deferredPost(index, max){
var delay = Math.random()*3;
if (index<max){
return $.post('/echo/html/', {html:('Response to '+args[index]), delay:delay},
function(data){
$('#response').append(data+'<br>');
}).then(function(){
deferredPost(index+1, max);
});
} else {
return $.post('/echo/html/', {html:('Response to '+args[index]), delay:delay},
function(data){
$('#response').append(data+'<br>');
});
}
}
DEMO
Here I used then function.
I also recommend to read a little bit more about deferred objects, they can solve a couple of common problems.
This is a job for a queue.
var queue = ['arg1','arg2','arg3','arg4','arg5','arg6'];
function runQueueInOrder() {
if (queue.length === 0) { return; }
var arg = queue.pop();
var delay = Math.random()*3;
$.post('/echo/html/', {html:('Response to '+ arg), delay:delay},
function(data){
$('#response').append(data+'<br>');
}).then(function() {
runQueueInOrder();
});
}
runQueueInOrder();
You don't need to use jQuery's then for this to work if you've encapsulated the processing of the queue in a function. It's handy though. The code is destructive as it removes elements from the original array (but as they are processed, it's usually OK).
The method runQueueInOrder is called to initiate processing.
When there is no more work to be done, the function simply exits. (I've written a version that polls on a timer before, but that's not needed here).
The function grabs the next work arg, calls your post call syntax, and when done uses jQuery's deferred then callback to call the function again (to process the queue further if needed).
(I looked at the other answer and found it confusing to follow, so I took a simpler approach. Using my simple version, you can add new items as new work is discovered--or remove them.).

Resources