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.
Related
I'm working with AngularJS 1.5 (I'm really beginner with JS) in a user profile view where the user can change a lot of things in his data.
In this view the data is split in several sections and save all of this data means to do several calls to server ( due to the design of app). So the problem that I found is that when the user modify his data maybe modify only a part of this and when it push save button I don't want call all methods, only to necessary methods.
I've programed the way to detect the changes in data blocks when the user push the save button, sometimes the controller make a call and in other cases two or three. But the problem is that the calls (made with $resource library) is executed asyncronously and I would like can control this better.
I would like do the next: store all calls in a list or array but wihout execute them and after execute all at the same time (more or less). If any of this fails I would like show a error message to user (only a generic error message), but internally log the call that failed, and in the same way only show (and only one) a success message when all calls have ended with success ( and not a message per success call).
I don't know how to do this, some mates say me that maybe I need use $q AngularJS service to do this, or store the promises that $resource have to execute all after (I've trying this without success) or work with promises in JS.
Anyone can give me any idea?
Finally I resolved my problem using $q. At first I wanted store the calls without execute them (I thought that it was better) but finally I can check that only stored the results of this calls is enought for my aim. So, this a skeleton of the solution that I've been done:
At the beginning of the controller
var promises = [];
In all places where I need make a controlled call inside of save user data function:
var deferred = $q.defer();
var promise = vm.teacher.$update(
function () { // Success
deferred.resolve('Success updating the teacher.');
},
function (error) { // Fail
deferred.reject('Error updating the teacher, error: ' + error)
});
promises.push(deferred.promise)
}
...
... vm.otherItems.$update ...
...
And at the end of this function, something like this:
$q.all(promises).then(
function(value){
console.log('Resolving all promises, SUCCESS, value: ')
console.log(value);
toastService.showToast('Success updating the teacher.');
// It deleted old promises for next iteration (if it exists)
promises = [];
},function(reason){
console.log('Resolving all promises, FAIL, reason: ')
console.log(reason);
toastService.showToast('Fail updating the teacher.');
}
)
Thanks for the help!
I concede that, despite hours of reading and attempting, I am fundamentally unable to grasp something about Deferred promises and asynchrony in general.
The goal on my end is real, real simple: send some data to the server, and react to the contents of the response conditionally.
The response will always be a JSON object with save and error keys:
{ "save": true, "error":false}
// or
{ "save" : false,
"error" : "The server has run off again; authorities have been notifed."}
I have tried dozens and dozens of variations from the jQuery API, from other stackexchange answers, from tutorials, etc.. The examples all seem concerned with local asynchronous activity. When I need is some ability to be made aware when the AJAX request has either finished and returned a response I can inspect and make decisions about, or else to know that it's failed. Below, I've used comments to explain what I think is happening so someone can show me where I'm failing.
I know this is a repost; I am, apprently, worse than on average at grasping this.
var postData = {"id":7, "answer":"Ever since I went to Disneyland..."};
/* when(), as I understand it, should fire an event to be
responded to by then() when it's contents have run their course */
var result = $.when(
/* here I believe I'm supposed to assert what must complete
before the when() event has fired and before any chained
functions are subsequently called */
/* this should return a jqXHR object to then(), which is,
I'd thought, a queue of functions to call, in order,
UPON COMPLETION of the asynchronous bit */
$.post("my/restful/url", postData))
.then( function() {
/* since "this" is the jqXHR object generated in the $.post()
call above, and since it's supposed to be completed by now,
it's data key should be populated by the server's response—right? */
return this.data;
});
// alas, it isn't
console.log(result.data);
// >> undefined
Most examples I can find discuss a timeout function; but this seems, as I understand, to be a failsafe put in place to arbitrarily decide when the asynchronous part is said to have failed, rather than a means of stalling for time so the request can complete. Indeed, if all we can do is just wait it out, how's that any different from a synchronous request?
I'll even take links to a new read-mes, tutorials, etc. if they cover the material in a different way, use something other than modified examples from the jQuery API, or otherwise help this drooling idiot through the asynchronous mirk; here's where I've been reading to date:
jQuery API: Deferred
JQuery Fundamentals
jQuery Deferreds promises asynchronous bliss (blog)
StackOverflow: timeout for function (jQuery)
Update
This is in response to #Kevin B below:
I tried this:
var moduleA = {
var moduleB = {
postData: {"id":7, "answer":"Ever since I went to Disneyland..."};
save: function() {
return $.post("path/to/service", postData, null, "JSON");
}
};
var result = this.moduleB.save();
result.done(function(resp) {
if (resp.saved == true) {
// never reached before completion
console.log("yahoo");
} else {
console.log("Error: " + resp.error);
// >> undefined
}
});
}
You are over-complicating your code. You cannot get the data to outside of the callback, no matter how many deferred/promises you create/use (your sample creates 3 different deferred objects!)
Use the done callback.
var postData = {"id":7, "answer":"Ever since I went to Disneyland..."};
$.post("my/restful/url", postData).done(function (result) {
console.log(result.save, result.error);
});
You seem to have a misunderstanding of both asynchronous requests, the Promise pattern, and Javascripts mechanism of passing functions as an argument.
To understand what's really happening in your code I suggest you use a debugger and set some breakpoints in the code. Or, alternatively, add some console.logs in your code. This way you can see the flow of the program and might understand it better. Also be sure to log the arguments of the function you pass as an argument in the then()-method, so you understand what is passed.
ok you got it half right. the problem is that when you execute the console.log the promised is not yet fulfilled the asynchronous nature of the promises allows the code to execute before that ajax operation is done. also result is a deferred not a value, you need to handle your promised with .done instead of .then if you wish to return a value otherwise you'll continue passing promises.
so that said
var result={};
$.when(
$.post("my/restful/url", postData))
.done( function(data) {
result.data=data;
});
// here result is an object and data is a undefined since the promised has no yet been resolve.
console.log(result.data);
My question is about readTextAsync and writeTextAsync in the context of windows store applications. I have searched StackOverflow and MSDN and also otherwise Googled extensively.
My code is given below:
Windows.Storage.ApplicationData.current.roamingFolder.getFileAsync("sample.txt")
.then(
function(samplefile){
return Windows.Storage.FileIO.readTextAsync(samplefile)
},
function(e){},
function(samplefile){
Windows.Storage.FileIO.readTextAsync(samplefile)
}
)
.done(
function(something){ data = something; },
function(){},
function(something){ data = something; }
);
My problem is that most of the time the file does not get read. When I debug, it gets read intermittently.
It appears to be an issue of not allowing enough time for the async call to complete.
I am totally new to Windows app programming and javascript.
I would appreciate any help. Thanks. ravi
Whan you chain many promises you should have one error function at the end where you put your "done".
In this way you will be able to see if there is an error while it is reading.
The way you should write it is:
Windows.Storage.ApplicationData.current.roamingFolder.getFileAsync("sample.txt")
.then(
function(samplefile){
return Windows.Storage.FileIO.readTextAsync(samplefile)
}
)
.done(
function(data){ //do something with your data, like assign to a list },
function(error){ //do something with error },
function(data){ //do something with your data } //progress function,not sure what you want to do with this
);
But this call may not be your problem, if you put this code in a normal function and then call it, you will not be able to see the data object because it will be loaded in an asynchronous way.
You have to process your data inside the done function because if you assing it to an external variable (your data object) like you did, that variable will be empty when you try to use because most likely the done method hasn't occured yet.
In the progress handler for then, I am just trying to repeat the call, to ensure completion.
That makes no sense. It might even lead to race conditions, since you try to read the file while getting it is still in progress. Also, the repeated call does not return anything, and the task will not be chained into / synchronized with the flow of the rest. Remove that handler.
The reason for the emptry error handler is I haven't decided what to do in case of an error.
You can just omit it then. Errors would then just "bubble" up, and can be caught in other promises that depend on the failed one.
I want the text that is read to be stored in data for processing later.
But when is "later"? You would need to ensure that the processing does not start before the file is completely read - for which you will need to hook a done handler on your promise. Do the processing in the promise chain as well, never use global/higher-scoped variables with promises. If you need data multiple times, you can simple store the promise in a variable, and attach multiple done handlers (which will work even when the promise is already resolved).
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).
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.