How can I make a jquery $.when statement refire if it fails? - javascript

So sometimes due to some kind of server asynchronicity issue, or something.. I don't know what, but it only happens every once in a while (s_mcir_2 calls its own AJAX functions that are cross-domain, so the other server I suppose can be unreliable).. Anyway, every once in a while result is returned from s_mcir_2 as null instead of a JSON object.
When this happens, I would like to test if it is null, and then if it is, have the $.when statement refire.. theoretically until it receives valid output.
Any ideas?
$.when(s_mcir_2(alt, data[l_alt])).then(function(result) {
//EVALUATE "result"
});

Deferred objects can only resolve once.
Try using a named function that calls itself on fail.
function myFn () {
$.when(s_mcir_2(alt, data[l_alt])).then(function(result) {
//EVALUATE "result"
if (!result) {
setTimeout(myFn,125);
log("R_MCIR_2 has failed");
}
else {
// success, do stuff
...
}
});
}

.then() can take two callback functions, one for done and one for fail. If you run your code inside a function you can recursively re-run your AJAX request (note that this could create an infinite loop):
var failedCount = 0;
function some_func() {
$.when(s_mcir_2(alt, data[l_alt])).then(
//done callback
function(result) {
//EVALUATE "result"
},
//fail callback
function () {
failedCount++;
if (failedCount < 10) {
//try again
some_func();
}
});
}
Docs for .then(): http://api.jquery.com/deferred.then
This code assumes that the s_mcir_2() function returns a jqXJR object (e.g. return $.ajax(...)).
Notice I added a counter to the failed function so this process won't continue infinitely. Once 10 requests have been made, the recursiveness stops.
UPDATE
If instead of the jqXHR object being rejected, if it is resolving to success you can check the server response and if it is null (e.g. typeof(serverResponse) == 'null') then re-run the some_func() function.

function attempt(){
$.when(s_mcir_2(alt, data[l_alt])).then(function(result) {
//EVALUATE "result"
}).fail(function(result) {
log("R_MCIR_2 has failed");
setTimeout(attempt,500)
});
}

Related

Call function after getting response from WS

I need to call three WS services before calling a local function depending on whether some variables are defined or not, but the function is getting called before the services get any response, because it could take some time. I've even tried with $timeout, but it does not work
$scope.$on('search', function (event, data) {
self.searchDto= data;
if (self.searchDto.userCode) {
self.searchByUserCode(self.searchDto.userCode).then(function (data) {
self.userCode= data.find(function (item) {
return item.mstId === self.searchDto.userCode;
});
});
}
if (self.searchDto.companyCode) {
self.serachByCompanyCode(self.searchDto.companyCode).then(function (data) {
self.companyCode= data.find(function (item) {
return item.mstId === self.searchDto.companyCode;
});
});
}
if (self.searchDto.jobCode) {
self.searchByJobCode(self.searchDto.jobCode).then(function (data) {
self.jobCode= data.find(function (item) {
return item.mstId === self.searchDto.jobCode;
});
});
}
//I tried with this timeout but it didnt work
$timeout(function () {
self.searchPeople();
}, 1000);
});
Does anyone have idea how the searchPeople method can be called after the WS responses?
Use promises and $q.all()
var promises = [];
promises.push(self.searchByUserCode(self.searchDto.userCode).then(function (data) {
self.userCode= data.find(function (item) {
return item.mstId === self.searchDto.userCode;
});
}));
.then() returns a promise. Do that for the 3 service calls and then wait for their completion
$q.all(promises).then(function(){
self.searchPeople();
})
I see that you might not call all of your services. $q.all() will wait for the promise you put in the array. Keep in mind it will also execute your call if none of your services has been executed, if you need at least one to be called, you might want to add a check for promises.length > 0 before $q.all().
That way, if you only call one of your services, the promises array will have one element and upon its completion, will call your local function.
Setting timeout is not a correct approaching here. One solution can be: you should put 3 WS nested and put the function call inside the last WS callback.
It also depends on how much arguments that your searchPeople need. If it only work with fully 3 arguments from WS calls, another solution is putting the function call in all 3 WS callback, and inside function searchPeople, you should add a condition statement to check if we have fully 3 argument before do searching

Get response of multiple deferred objects in jquery

Below are my Multiple ajax calls with promises.
$(window).load(function(){
$.when(getApiModemList()).done(function(vendors){
var deferreds = calculateApiBalances(vendors);
$.when.apply($,deferreds).done(function(balance) {
console.log(balance);
console.log("All Done");
});
});
function getApiModemList(){
return $.getJSON("url");
}
function calculateApiBalances(vendors)
{
var defer=[];
$.each(vendors,function(k,v){
defer.push($.getJSON(someurl));
});
return defer;
}
});
Function calculateApiBalances() return me some balance which I need to Sum up to get total of all balance .
But when print console.log(balance) it doesnot provide me with valid array of balance json.
Another issue is if any one of my ajax calls in calculateApiBalances() fails it doesnot prints All Done.
What should be done in above code to achieve this.
But when print console.log(balance) it doesnot provide me with valid array of balance json.
That's a weird thing of $.when. It doesn't provide you with an array, but calls your callback with multiple arguments.
Another issue is if any one of my ajax calls in calculateApiBalances() fails it doesnot prints All Done.
Yes, because when one promise fails the whole $.when() promise is rejected immediately, and you don't have any error handler. You'll have to catch errors individually if you want to always get an array (of possibly invalid responses). See also $.Deferred: How to detect when every promise has been executed.
What should be done in above code to achieve this.
First of all, avoid the deferred antipattern :-)
$(window).load(function() {
getApiModemList().then(calculateApiBalances).then(function() {
var balance = Array.prototype.slice.call(arguments);
console.log(balance);
console.log("All Done");
});
});
function getApiModemList() {
return $.getJSON(somurl).then(function(res) {
return res.data;
});
}
function calculateApiBalances(vendors) {
return $.when.apply($, $.map(vendors, function(v, k) {
return $.getJSON(someurl).then(null, $.when);
}));
}
EDIT by Roamer:
And here's a version of the master routine with a mechanism for summing the balances.
getApiModemList().then(calculateApiBalances).then(function() {
var sumOfBalances = Array.prototype.reduce.call(arguments, function(tot, obj) {
return tot + (+obj.balance || 0);
}, 0);
console.log(sumOfBalances);
console.log("All Done");
});
obj is the object promised by $.getJSON(someurl) in calculateApiBalances(). In the case of a $.getJSON() error, obj will be a jqXHR object, obj.balance will be undefined and +obj.balance will be NaN, therefore default to adding zero; otherwise add obj.balance .
If you wanted to know how many of the getJSON requests were successful and how many were unsuccessful, then there's some more code to write, but not a lot.

check that a task is completed

I have a javascript function which supposed to check whether a task is completed.
When the task is completed there is a completion record in a file on the server.
The function supposed to make recursive calls to the server with some delay (potentially increasing) till it gets the completion record in the file.
The code given below makes excessive calls to the server with interval less than a second
example from Web Console:
[20:06:21.202] [20:06:21.563] [20:06:21.990]
But the task becomes competed on variable waittime value getting equal to max_waittime .
Though for a test case overall output is as expected, something is wrong with the function.
Where I'm wrong?
function check_status(time,div_id,filename) {
var status =0;
var waittime=time;
var max_waittime=11000000;
if (waittime < max_waittime){waittime=waittime+1000000; }
$.ajax({
type: "GET",
async: false,
url: "code_on_server_checking_file.php",
data: "f="+filename,
dataType: "text",
success: function(content) {
if (content ) {
// stuff related to output of the result
....
return status=1;
}
else {return status=0;}
}
});
if (status == 0 && waittime < 20000000){
setTimeout(check_status(waittime,div_id,filename),waittime);
}
else {alert('check_status passed!'+status+'|'+waittime);}
}
You need to pass check_status to setTimeout, not the value returned by invoking check_status(...). Since you need to pass parameters to check_status, use an anonymous function:
setTimeout(function () {
check_status(waittime, div_id, filename);
}, waittime);
You are calling the function instead of giving it as a reference to setTimeout. Wrap your function call in an anonymous function. Also, it would be better to simply set up the call in the ajax callback if needed rather than using a synchronous call. A synchronous call will tie up your browser.
function check_status(time,div_id,filename) {
$.ajax({
type: "GET",
url: "code_on_server_checking_file.php",
data: "f="+filename,
dataType: "text",
success: function(content) {
if (content ) {
// stuff related to output of the result
}
else {
time += 1000000;
if (time < 20000000) {
setTimeout( function() { check_status( time, div_id, filename); }, time );
}
}
}
});
}
"recursive calls to the server"? No, I don't think you want that.
If you go three deep, var max_waittime=11000000; will be created and initialized three times.
Maybe you can set the timeout value for the ajax call (ajax settings)
http://api.jquery.com/jQuery.ajax/
First of all, it looks like you don't understand that the ajax call is an asychronous call. Calling it just starts the networking operation and then the rest of your code continues executing. Some time later when the networking operation completes, your success function is called.
The ONLY place you can operate on the results of the ajax call is in the success function. You can't return a value from the success function and expect that to go anywhere. The only place that goes is somewhere inside the ajax code where it's dropped. If you need to do something with the results of the ajax call, then you need to either do that operation right in the success function or call some other function from the success function and pass it the returned data.
These are the parts of your code that do not work:
There's no point in returning the status value from the success function. It doesn't go anywhere except into the ajax function where the return value is just dropped.
This line of code if (status == 0 && waittime < 20000000){ is not doing what you want. Because the ajax call is asynchronous, the value of status has not yet been set by the ajax call when this line of code runs. Thus, it's ALWAYS 0 so your logic never works. You need to move this logic inside the success handler.
As others have said, your parameters to setTimeout are not right. You have to pass a function to setTimeout, not the results of executing a function.
This is the code I would suggest:
function check_status(time, div_id, filename) {
var max_waittime=11000000;
if (time < max_waittime){
time=time+1000000;
}
$.ajax({
type: "GET",
async: false,
url: "code_on_server_checking_file.php",
data: "f="+filename,
dataType: "text",
success: function(content) {
if (content ) {
// stuff related to output of the result
if (time < 20000000){
setTimeout(function() {check_status(time, div_id, filename)}, time);
}
}
}
});
}
Note that all handling of the ajax result is done in the success function and we pass an anonymous function to setTimeout that re-calls check_status after a time delay. This is not actually recursion (as others mentioned) because setTimeout allows check_status to return before it's called again some time later.

Need help with Extjs function returning undefined results

I want to do the following with my javascript codeblock.
Handle all current and new device requests ie. detect,encrypt,decrypt,etc
Return the result to the calling method
Questions
How can I improve the existing code and get rid of the javascript strict warning:anonymous function does not always return a value.
What is the right way of calling my method?
Any help is greatly appreciated
Thanks!
Herewith the code:
This is how I call the current method
//Contents of SmEditor.js
var response = Ext.decode(Prometheus.DeviceRequestHelper.detect(request_id));
//contents of Sm.js
Ext.ns('myApp')
myApp.DeviceRequestHelper = {
detect:function(request_id){
var task = function(){
Ext.Ajax.request({
url: 'device_requests.php',
params:{
action:'get_device', //in php
'request_id':request_id
},
timeout:30000, //30 seconds
success:function(response){//serverside response
var result = Ext.decode(response.responseText); //convert to js objects
if(result.success == true){//device was detected
cons.log('success,device was detected');
cons.log(result);
Ext.TaskMgr.stop(runTask);
return Ext.encode(result); //javascript strict warning
}else{
if(runTask.taskRunCount >= 10){
//retry limit exceeded
Ext.Msg.show({
title:'Server Failure',
msg:"Detection Failed,Unable to detect device",
icon: Ext.MessageBox.ERROR,
buttons: Ext.Msg.OK
});
Ext.MessageBox.getDialog().getEl().setStyle('z-index','80000');
Ext.TaskMgr.stop(runTask);
}
}
},
failure:function(response){
Ext.TaskMgr.stop(runTask);
Ext.Msg.show({
title:'Server Failure',
msg:"Failed, server communication error",
icon: Ext.MessageBox.ERROR,
buttons: Ext.Msg.OK
});
Ext.MessageBox.getDialog().getEl().setStyle('z-index','80000');
}
})
}
var runTask = {
run: task,
interval:2000,
repeat:10
};
Ext.TaskMgr.start(runTask);
}
}
To prevent this kind of warning, have the function return a value in all cases, or no cases. At the moment you're only returning a value in one if case; the other cases will not return anything. You can even return undefined to make the warning go away. However, what it is telling you is correct: that a function that sometimes has a return value and sometimes doesn't is a bit weird and suggests you're doing something wrong.
What you seem to want to do is have the inner return in the success method return a value from the detect() method. This is absolutely not possible. The inner function can only return a value to the caller of success, which is Prototype itself. By the time this happens, the detect() method has long since returned.
What you have here is asynchronous code. The detect() method can set up an AJAX request, but it must then return immediate to its caller, which will return control to the browser. At some later time, the HTTP request behind the AJAX call will complete, and then the success function will fire. JavaScript cannot call asynchronous code synchronously, or vice versa.
What you have to do is pass a callback function into your method, and then call it back on completion:
Prometheus.DeviceRequestHelper.detect(request_id, function(response) {
// do something with `response`
});
myApp.DeviceRequestHelper= {
detect: function(request_id, callback) {
...
Ext.Ajax.request({
...
success: function(xhr) {
var result= Ext.decode(xhr.responseText);
if (result.success)
callback(result);
...
},
...
});
},
...
};
(I removed the extra Ext.encode->Ext.decode pair, that just seems like a waste of time.)
First, Your detect method will not return a value and will return immediately(even before the ajax call completes) because the ajax call is asynchronous
Second, there's no point returning a value in your success handler. Instead you should provide a callback function to your detect method like so:
Ext.decode(Prometheus.DeviceRequestHelper.detect(request_id, function(response) {
// do something with your response
}));
// detect function takes a callback function as a parameter
myApp.DeviceRequestHelper = {
detect:function(request_id, funCallback){ // pass in a callback function that is
// called when result was a success
var task = function(){
Ext.Ajax.request({
url: 'device_requests.php',
params:{
action:'get_device', //in php
'request_id':request_id
},
timeout:30000, //30 seconds
success:function(response){//serverside response
var result = Ext.decode(response.responseText); //convert to js objects
if(result.success == true){//device was detected
cons.log('success,device was detected');
cons.log(result);
Ext.TaskMgr.stop(runTask);
// return Ext.encode(result); //javascript strict warning
funCallback(Ext.encode(result)); // ===========> callback function called.
}else{
if(runTask.taskRunCount >= 10){
//retry limit exceeded
Ext.Msg.show({
title:'Server Failure',
msg:"Detection Failed,Unable to detect device",
icon: Ext.MessageBox.ERROR,
buttons: Ext.Msg.OK
});
Ext.MessageBox.getDialog().getEl().setStyle('z-index','80000');
Ext.TaskMgr.stop(runTask);
}
}
},
failure:function(response){
// ... failure handing code
}
});
}
var runTask = {
run: task,
interval:2000,
repeat:10
};
Ext.TaskMgr.start(runTask);
}
}

Jquery $.post() variable scope

I'm not massively experienced with JavaScript and I'm having trouble with variable scope and jquery. I have the following structure:
function pass_variables()
{
username = "efcjoe"
response = post_variables(username)
alert(response)
}
function post_variables(username)
{
$.post(
'/path/to/url/',
{
'username': username,
},
function(data)
{
valid = (data != 0) ? true : false
// OPTION 1: If I put return here...
return valid; // ... the alert box in pass_variables says "undefined"
},
"text"
);
// OPTION 2: If I put return here...
return valid; // ... The alert box does not pop up, and Safari debug gives
// me the error: "Can't find variable: valid"
}
Am I missing something there? I think valid should be a global variable, and therefore option 2 should work fine. I'm really not sure about option 1.
Can anyone give me any advice on the best way to get this working?
Thanks a lot.
Ajax calls are asynchronous which means they get called but do wait around for execution to complete. Basically your alert is firing before the ajax request has completed and run the callback function to change your variable.
The best thing you can do is actually pass a function to run when the ajax request has completed. This also negates the need for global variables which are frowned upon since other plugins, script can alter their state and leave your script open to errors, flaws etc
E.g
function foobar(){
//call function to do post request and also pass a function to run
//when post has returned
runPostRequest( callbackFn );
}
function runPostRequest(callback){
$.post( '/foo', callback );
}
function callbackFn( data ){
console.log('post request complete');
}
In your option 1 you are returning from the callback function, and its return value is never used because this function is only called when the Ajax request ends.
In the option 2, you are returning from your main function, but that return happens before the callback function assign any value to your valid variable.
I would refactor your code in this way, without using global variables:
function post_variables(username){
$.post('/path/to/url/',{
'username': username,
},
function(data){
var valid = data != 0;
// OPTION 3: Work in the callback function
alert(username);
alert(valid);
// OPTION 4: Pass the values and work on another function
otherFunction(username, valid);
},"text");
}
function otherFunction(username, isValid){
//...
}
Yeah, your problem is that you're not grasping some order of operations issues here. The function you're passing to $.post is a callback; it runs later, considerably after post_variables() finishes. post_variables() itself does not wait for the $.post to finish, so valid doesn't exist when you're trying to use it.
Remember that AJAX is asynchronous. The return valid; gets executed immediately after the $.post() is set up, but before the post has completed (and therefore, before valid is defined). What you probably want to do is this:
function post_variables(username)
{
var username = "efcjoe";
$.post(
'/path/to/url/',
{
'username': username,
},
function(data)
{
var valid = (data != 0) ? true : false
alert(valid);
},
"text"
);
}
And note that this no longer needs global variables, but function-scope variables that are declared using var.
You could solve the problem quite easily by assigning it a function instead of an inline one, and the event function does the alert:
function pass_variables()
{
username = "efcjoe"
response = post_variables(username);
}
function post_variables(username)
{
$.post(
'/path/to/url/',
{
'username': username,
},
receivedData,
"text"
);
}
function receivedData(data)
{
valid = (data != 0) ? true : false;
alert(valid)
}

Categories