I have a settings page on my jQuery mobile website When user clicks save button, I update the server for 3 different user inputs i.e language, currency, threshold
In order to do this I make 3 separate ajax calls (with PUT). So when all are succesfull I go to another page if any of those fails I stay on the same page and show the error messages.
The problem is I only want to switch the page if all calls are succesful, and if there are any errors I want to show one alert with all messages (rather than 3 separete alert windows), so I need to wait the results of all these calls.
To achive this in all 3 Ajax calls I used;
async:false
And I put a boolean in all these calls succes methods like;
success: function (data){
languageUpatesuccesful=true;
}
and then something like this;
if(languageUpatesuccesful){
make the next call to update currency..etc
}
...
if(allsuccesful(){
changepage();
}
So I can track when exactly when one calls finishes then I make the next call, if all succesful switch to another page.
While this works, I think this is a horrible solution, is there a way to achive this by using async:true ?
Because disabling asynchrous ajac freezes the page and I cant even show animations, also it is not recommended by jQuery. But then how can I know when these 3 calls are finished and take action depending on result?
Use deferred objects together with $.when:
$.when(ajax1(), ajax2(), ajax3()).done(function() {
// all Ajax calls successful, yay!
}).fail(function() {
// oh noes, at least one call failed!
});
Where ajaxX is defined as:
function ajax1() {
return $.ajax(...);
}
If you indeed want to chain the Ajax calls instead of having them concurrent, have a look at Bergi's solution.
You can easily chain them by using the Deferred interface:
$.ajax({…})
.then(function(languageUpateResult) {
return $.ajax({…});
})
.then(function(currencyUpdateResult) {
return $.ajax({…});
})
.then(function(thresholdUpdateResult) {
changePage();
});
Sorry, I skipped the fact that the ajax calls are separate. The above code executes them sequentially, if you just want to execute them in parallel and wait for all of them to finish use $.when() - see #FelixKling's answer.
You can try using web workers to do your async calls in a background thread. Web workers have pretty good browser support and should solve your issue. If you are interested, I have written a little abstraction of the web worker library to make them easier to work with (just send me a pm).
Related
I downloaded a library called jsdeferred to try and help me with some code-flow problems, but I am a little lost, as its examples and ...'documentation' is a little unclear on some things. But as I kept reading and digging, and of course googling everything under the sun, I also found out jQuery has its own Deferred() system. I am linking both here, for proper context.
Link to jsDeferred Library
Link to jQuery.Deferred()
The Problem
I need to find a way to tell the page to "hold on until the last thing is done".
This is what thought jsdeffered did. So part of my question is asking which should I use? jsDeferred or jQuery.Deferred(); and then how to use it as I've outlined below.
The Situation
My scenario is this, in a nutshell, I need to perform the following behavior.
page loads, a view model is defined
This is using kendo ui mvvm to declare my view model, so it is a kendo.data.ObservableObject
an $.ajax call is made to the database to get some default model data
This is where I am getting the most trouble. I need everything to "hold on" until this $.ajax is done. But I don't want to wrap everything in the $.ajax().done(r) if I can help it. That looks/feels very sloppy to me and is kind of confusing at times.
other widgets on the page are rendered, they have respective database queries done through kendo ui Remote DataSource.
These are actually working as intended.
jQuery Validate is wired to the view, with defaults having been set already.
This is also working as intended.
kendo.bind('body', viewModel); is called to perform model binding.
Now this is where I am running into trouble, going back to step 2 where I was making the $.ajax call. What keeps happening is that kendo.bind is fired before the $.ajax completes. I can put it in the $.ajax({}).done(); function, and for this exact one specific page that does work, but there will be many other situations where that isn't suitable.
What I have tried
First, I'll be clear that the jsdeferred documentation is very unclear to me, as running its samples verbatim doesn't actually work. I am continuously told that next is not defined and the like. I eventually figured out that you have to have an implicit Deferred. before you call next the first time.
So here is what I thought would happen...
var viewModel = new kendo.data.ObservableObject({
// various view model properties defined
});
Deferred.define();
next(function() { // let's call this STEP 1
$.ajax({
// data for ajax to controller
}).done(function(result) {
// perform operations with result
});
}).
next(function() { // let's call this STEP 2
$('#dropdownlist_target').kendoDropDownList({
// parameters, remote data source for drop down list, etc.
}).data("kendoDropDownList");
}).
next(function() { // let's call this STEP 3
$('form').validate({
// any extra form validation stuff
});
}).
next(function(){ // let's call this STEP 4
kendo.bind('body', viewModel);
});
I believed that these would each run one, right after the other, when the previous one is finished. But that is not what is happening. STEP 1 is still in the process of fetching while STEP 2, 3 and 4 are running.
This doesn't seem to be any different than the way the code was running without the jsdeferred library. So I am very confused and would absolutely love some help here. I need STEP 1 to be completely finished before STEP 2 fires, basically.
The problem is that next() expects you to return the thing you want it to wait for. In step one, you're not returning anything. jsdeferred is therefore assuming you were performing a synchronous operation (that has already finished), and so it continues with step 2.
Instead, return the jQuery.Deferred() returned from the $.ajax() call. jsdeferred will then wait for that to complete before it executes step 2.
Regardless of this, I'd dump jsdeferred. As you've realised, jQuery has a fully fledged Deferred implementation. I'm not sure what jsdeferred brings to the party.
Using $.ajax().done(r) is not sloppy. Asynchronous behaviour is the core of event driven languages, and JavaScript is one. Embrace it, or you'll go bald very early in life trying to avoid it.
If you revert to jQuery's Deferred implementation, you might like then(), to give you the semantics of next();
$.ajax({
// data for ajax to controller
}).done(function(result) {
// perform operations with result
}).then(function () {
$('#dropdownlist_target').kendoDropDownList({
// parameters, remote data source for drop down list, etc.
}).data("kendoDropDownList");
$('form').validate({
// any extra form validation stuff
});
kendo.bind('body', viewModel);
}).then(function () {
// Note you can chain then()'s as well.
});
You can just use the then method on your $.ajax() result in the same way you're using jsDeferred's next helper. Generally speaking, then is a more flexible method than done. And as Matt noted in his answer, it's a common mistake in promise based programming to forget to return a new promise within the handler, causing it to resolve prematurely with undefined instead of waiting on a the new promise.
$.ajax({ // let's call this STEP 1
// data for ajax to controller
}).
then(function(result) {
// perform operations with result
}).
then(function() { // let's call this STEP 2
$('#dropdownlist_target').kendoDropDownList({
// parameters, remote data source for drop down list, etc.
}).data("kendoDropDownList");
}).
then(function() { // let's call this STEP 3
$('form').validate({
// any extra form validation stuff
});
}).
done(function(){ // let's call this STEP 4
kendo.bind('body', viewModel);
});
Note that in my refactoring, all of those thens will execute immediately in a row, unless a new promise is returned. So you may as well combine them.
then takes a function that either returns a value or a promise, and it returns a new promise. If its function returned a value, the new promise is immediately resolved with that value. If its function returned a promise, then that promise is passed through as the new promise. Note that jQuery's then only works this way as of jQuery versions >=1.8.
There is synchronization issue between localstorage and server.
Countries,Cities,Users are synchronized with server seperately.(Seperate ajax calls)
The problem is other Javascript codes (Kncokout Bindings and etc.) must wait synchronization process. Or better a javascript function have to wait another one.
How can I implement it?
PS: I'm using jquery,amplifyjs (for localstorage interaction),knockoutjs libraries.
PS2: I need cross browser solution :)
EDİT
Summary:
I have 3 Javascript functions. All of them makes asynchronous ajax call with callback.
These functions could work as parallel.(Don't have to wait each others)
But the code have to work after these 3 functions.(Because these functions are synchronizing local storage and the code uses localstorage)
Use deferred objects and $.when, that's one of it's primary uses.
$.when($.ajax(...),$.ajax(...),$.ajax(...)).done(function(){
// all three are done
});
Here is one way you can implement this, provided that you are initiating the ajax calls at the same time. A simple count-track can solve it -
var countCalls;
Then in your ajax related code:
function initAjaxCalls() {
countCalls = 0; //(re)set counter
startAjax1(commonCallback); //pseudo call, use commomCallback as callback
startAjax2(commonCallback);
startAjax3(commonCallback);
}
On the callback we keep track of number of calls:
function commonCallback(e) {
countCalls++;
/// when we have reached the max count, perform the sync. step
if (countCalls === 3) performSyncStorage();
}
This will wait until the longest lasting operation has finished. This is necessary if you want to sort of convert an asynchronous operation into a synchronous one.
I am building a client-side application where I send requests to YouTube API, Instagram API, Tumblr API. When those requests are processed, an appropriate callback function is executed.
The problem is YouTube always finishes first, appends data to HTML via underscore.js templates. Then a few seconds later Instagram and Tumblr results are also appended.
My app.js file contains 3 separate plain javascript functions:
function showYoutube() { ... }
function showInstagram() { ... }
function showTumblr() { ... }
How could I display a simple "Loading..." message until all 3 callback functions have been successfully completed?
Update:
Still looking for a possible solution. Please note I do not have back-end service, thus I am limited to JSONP API requests only.
My API call is located in index.html and looks something like this:
<script
type="text/javascript"
src="http://gdata.youtube.com/feeds/users/GoogleDevelopers/uploads?v=2&alt=json-in-script&format=5&callback=showMyVideos">
</script>
So, function showMyVideos() has to be in the global scope. I've tried implementing jQuery Deffered and Async.js Parallel with no luck.
You could also use the pretty brilliant async library that also works client side.
In this case it'd be best to use sync JSONP calls because well async will implement and control the async part for you as such.
//show loading icon
async.parallel([
function (callback) {
showYoutube() // have showYoutube() declared outside async.parallel
// otherwise it will be inaccessible to the rest of your app.
callback()
},
function (callback) {
//send off to instagram
callback()
},
function (callback) {
//send off to tumblr
callback()
}
], function () {
//all are done, hide loading icon
})
Use a promise pattern, where you can fire a function/callback after n items have finished executed. Thus, you can display a spinner, fire your three async functions, and then upon completion of all three, fire a function to remove the spinner.
jQuery has promises and if you're using underscore, I think you can use underscore.deferred (I believe... I use jQuery)
I've got a couple of questions about this small snippett adapted from a tutorial I found here.
var loader = (function ($, host) {
return {
loadTemplate: function (path) {
var tmplLoader = $.get(path)
.success(function (result) {
$("body").append(result);
})
.error(function (result) {
alert("Error Loading Template");
}) // --> (1) SEMICOLON?
// (2) How does this wire up an event to the previous
// jQuery AJAX GET? Didn't it already happen?
tmplLoader.complete(function () {
$(host).trigger("TemplateLoaded", [path]);
});
}
};
})(jQuery, document);
Is there supposed to be a semicolon there?
It seems like the AJAX GET is happening and then an event is getting wired to it - what am I missing here?
Is there supposed to be a semicolon there?
It's optional, but recommended.
It seems like the AJAX GET is happening and then an event is getting wired to it - what am I missing here?
AJAX is asynchronous, so it's very unlikely the request will be already completed right after sending it. So, there's time to add another callback. And even if there weren't, it would work anyway, since jQuery implements those callbacks with promises. See example here.
With javascript, and ajax in particular it is important to understand how the browser goes about executing your code. When you make the request for remote data via an ajax GET, the rest of your code is still executing. Imagine if as soon as you made a request for some JSON to a busy server, lets say it takes a couple seconds, and everything on your page stops working during that time period. It would be very difficult to write code that wasn't difficult for the user to interact with. Luckily ajax is async, meaning it makes the request and an carries on as usual until the complete event (or equivalent) is fired. This is what executes your code pertinent to the data you just received. So when you specify that callback at the bottom of your snippit, you are telling the browser, "go do your thing for now but when you hear back from the server, do all of these things".
Oh yeah, and semicolons are optional, but as a best practice, most people use them.
They are assigning the $.get to a variable and then adding a complete handler to it.
It's the same as doing this:
$.get('/path'), function(){
//success callback
}).error(function(e){
//errors
}).complete(function(){
//always run
});
Just an unusual way of doing it.
Is there a way to listen for a javascript function to exit? A trigger that could be setup when a function has completed?
I am attempting to use a user interface obfuscation technique (BlockUI) while an AJAX object is retrieving data from the DB, but the function doesn't necessarily execute last, even if you put it at the end of the function call.
Example:
function doStuff() {
blockUI();
ajaxCall();
unblockUI();
};
Is there a way for doStuff to listen for ajaxCall to complete, before firing the unBlockUI? As it is, it processes the function linearly, calling each object in order, then a separate thread is spawned to complete each one. So, though my AJAX call might take 10-15 seconds to complete, I am only blocking the user for just a split-second, due to the linear execution of the function.
There are less elegant ways around this...putting a loop to end only when a return value set by the AJAX function is set to true, or something of that nature. But that seems unnecessarily complicated and inefficient.
However you're accomplishing your Ajax routines, what you need is a "callback" function that will run once it's complete:
function ajaxCall(callback){
//do ajax stuff...
callback();
}
Then:
function doStuff(){
blockUI();
ajaxCall(unblockUI);
}
Your AJAX call should specify a callback function. You can call the unblockUI from within the callback.
SAJAX is a simple AJAX library that has more help on how to do AJAX calls.
There's also another post that describes what you're looking for.
You can do a synchronous xhr. This would cause the entire UI block for the duration of the call (no matter how long it might take).
You need to redesign your program flow to be compatible with asynchronus flow, like specifying a callback function to be called after the response is processed. Check out how Prototype or JQuery or ... accomplishes this.
The answer is simple, you have to call unblockUI() when your ajax request returns the result, using jQuery you can do it like this:
function doStuff(){
blockUI();
jQuery.ajax({
url: "example.com",
type: "POST", //you can use GET or POST
success: function(){
unblockUI();
}
});
}
It sounds to me that you want the user to wait while info is being fetched from the db. What I do when I make an Ajax call for some info from the database is to display an animated gif that says "getting it..." - it flashes continually until the info is retrieved and displayed in the webpage. When the info is displayed, the animated gif is turned off/hidden and the focus is moved to the new info being displayed. The animated gif lets the user know that something is happening.