Javascript: Break from loop - javascript

I want to keep checking a list until it finds an item that is available. For each item in the list it will make a post request to check if the item is available. I want to keep it asynchronous. Also note that i am using ajaxq function not ajax function, that is because i am using this queuing script http://code.google.com/p/jquery-ajaxq/. So it won't be over before it's started sorta-thing. I'm sure thats not the problem though.
I need a way to break out of the loop once a item is available, so i can't just use a callback function because it won't be able to break out of the loop inside a function.
So i thought incrementing a variable if its done and using a do-while loop would work, but it just freezes my browser like it's a never-ending loop.
Any suggestions on how do fix this or do it a better way would be great.
do {
var d = 0;
for(var i in list) {
var item = list[i];
$.ajaxq('queue', {
type: 'POST',
url: baseURL + '/ChangeItem/Check',
data: {
'newItem': item,
'purchaseItem': false
},
error: function(jqXHR, textStatus) {
alert(textStatus);
},
dataType: 'text',
success: function(data) {
if(thisObject.isNotTaken(data)) {
d++;
thisObject.claimItem();
}
}
});
}
} while(d == 0);

You can use a recursive function:
function ChangeItem(list, index) {
var item = list[index];
$.ajaxq('queue', {
type: 'POST',
url: baseURL + '/ChangeItem/Check',
data: { 'newItem': item, 'purchaseItem': false },
error: function(jqXHR, textStatus) { alert(textStatus); },
dataType: 'text',
success: function(data) {
if(thisObject.isNotTaken(data)) { thisObject.claimItem(); doWhateverYouWantNext(); }
else ChangeItem(list, index+1);
}
});
}

The fact that the requests will be queued only guarantees that they will be executed seuqentially, and that the first request will have finished before the second starts. It does not mean that your code to enqueue the second request will wait until the first request is finished. So ajaxq does not help you either way here. You'd have to fall back to a recursive function, that invokes itself from the AJAX callback.
Having said that, you'll notice this'll cause a series of requests to your server, and presumably a series of database lookups. You may find that it'd be a much neater approach to send teh entire list of items to the server, and return the first match from there.

Please try this:
var d = 0;
for(var i in list) {
if(d == 0)
{
var item = list[i];
$.ajaxq('queue', {
type: 'POST',
url: baseURL + '/ChangeItem/Check',
data: {
'newItem': item,
'purchaseItem': false
},
error: function(jqXHR, textStatus) {
alert(textStatus);
},
dataType: 'text',
success: function(data) {
if(thisObject.isNotTaken(data)) {
d++;
thisObject.claimItem();
}
}
});
}
}

Related

Show waiting dialog on synchronous ajax

I want to show a waiting dialog while a synchronous ajax is made.
I using a Smart Wizard, to change between step one to step to i have to validate some data to do that i have to make 3 ajax call one after the other and while this is done i want to show a waiting dialog. This is what I'm doing.
if (indexes.fromStep==1) {
res=false;
var validatorResult = validator.checkAll($("#install_modbus_form"))
if (validatorResult) {
$("#modal_loader").modal()
$.ajax({
type: "post",
url: url1,
async: false,
dataType: "json",
data:{
data
},
success: function(response)
{
if (response.success)
{
$.ajax({
type: "post",
url: url2,
async: false,
dataType: "json",
data:{
data
},
success: function(response)
{
if (response.success)
{
$.ajax({
type: "post",
url: url3,
async: false,
dataType: "json",
data:{
data
},
success: function(response)
{
if (response.success)
{
//make magic here
res=true;
}
},
failure:function()
{
waitingDialog.hide()
res=false
},
error:function(a,b,c) {
waitingDialog.hide()
res=false
}
)
}
},
failure:function()
{
waitingDialog.hide()
res=false
},
error:function(a,b,c) {
waitingDialog.hide()
res=false
}
)
}
},
failure:function()
{
waitingDialog.hide()
res=false
},
error:function(a,b,c) {
waitingDialog.hide()
res=false
}
)
$("#modal_loader").modal('hide')
return res;//if true change step
}
}
I have trie use beforeSend to show the waiting dialog, also i have trie to use setTimeout but the waiting dialog is not show and the smart wizard dont go forward
Hope you can help, Im new in jquery.
Sorry for the bad english
On the assumption that you are using jQuery-Smart-Wizard, the solution lies in :
the construction of your onLeaveStep event handler, and (or including)
a modified version of the validation code shown in the question.
Fortunately, even though the plugin does not natively support asynchronism, it is fairly simple to make it do so. Essentially, what you need to do is :
to return false from the onLeaveStep callback,
to establish a promise which fulfills on successful validation, or rejects on failure,
to call .smartWizard('goForward') from the promise's success handler,
to call .smartWizard('showError') from the promise's error handler.
Based on smartWizard's ReadMe.md, here's a framework for performing synchronous and asynchronous validations :
$(document).ready(function() {
var waitingDialog = $('#whatever'); // ???
// Smart Wizard
$('#wizard').smartWizard({
onLeaveStep: leaveAStepCallback,
onFinish: onFinishCallback
});
function leaveAStepCallback(obj, context) {
alert("Leaving step " + context.fromStep + " to go to step " + context.toStep);
var returnValue;
switch(context.fromStep) {
case 1: // asynchronous
if (validator.checkAll($("#install_modbus_form"))) {
$("#modal_loader").modal();
waitingDialog.show();
validateStep1() // validateStep1() returns a promise
.then(function() {
// You will arrive here only if all three ajax calls were successful and all three responded with a truthy `response.success`.
$('#wizard').smartWizard('goForward'); // advance to next step
}, function(e) {
// You will arrive here on validation failure
$('#wizard').smartWizard('showError', e.message); // something went wrong
}).always(function() {
// You will arrive here on validation success or failure
waitingDialog.hide(); // the waiting is over
$("#modal_loader").modal('hide'); // ???
});
} else {
$('#wizard').smartWizard('showError', 'validator.checkAll() failed');
}
returnValue = false; // *must* return false to remain at step 1. If validation is successful, `.smartWizard('goForward')` will be executed later (see above).
break;
case 2: // synchronous
returnValue = validateStep2(); // validateStep2() returns true of false
break;
case 3:
...
break;
}
return returnValue; // true or false
}
// And here's the all-important `validateStep1()` :
function validateStep1() {
var sequence = [
{ url: 'url/1', data: {...} },
{ url: 'url/2', data: {...} },
{ url: 'url/3', data: {...} }
];
return sequence.reduce(function(promise, item, i) {
return promise.then(function() {
return $.ajax({
'type': 'post',
'url': item.url,
'dataType': 'json',
'data': item.data
}).then(function(response, textStatus, jqXHR) {
return response.success ? response : $.Deferred().reject(jqXHR, 'response.success not truthy at validation stage ' + i); // note: need to mimic jQuery.ajax's error signature.
});
});
}, $.when()) // starter promise for the reduction
.then(null, function(jqXHR, textStatus, errorThrown) {
return $.Deferred().reject(new Error(textStatus || errorThrown));
});
}
function validateStep2() {
// if validation here is synchronous, then return true of false
if(....) {
return true;
} else {
return false;
}
}
function validateStep3() {
...
}
// etc.
function onFinishCallback(objs, context) {
if(validateAllSteps()) {
$('form').submit();
}
}
function validateAllSteps() {
var isStepValid = true;
// all step validation logic
return isStepValid;
}
});
Notes :
the branching logic is in the onLeaveStep callback.
validateStep1() uses a chained promise pattern to sequence the three ajax calls.
if validateAllSteps() needs to repeat the step1 validation, then you will need call validateStep1().then(...) again, or chain from a previously cached promise.
As you can see, some aspects above are incomplete so there's still some work to do.

Sequentially execute two functions with jQuery in for loops

I'm pretty new to Javascript. Please don't make it too harsh :)
I have two functions, both of which involve executing jQuery requests within for loops. For example,
function a(n,locations) {
for (var i = 0; i < n; i ++) {
$.ajax({
url: 'https://geocoder.cit.api.here.com/6.2/geocode.json',
type: 'GET',
dataType: 'jsonp',
jsonp: 'jsoncallback',
data: {
searchtext: input,
app_id: APP_ID,
app_code: APP_CODE,
},
success: function (data) {
handleData(data,locations);
}
});
}
The handleData() function would make changes to the empty array locations from the jQuery data. My function b(m) is of similar format but would use the updated locations as input.
Now, I have a c(n,m) in which I would like execute a() and b() sequentially:
function c(n,m) {
var locations = [];
a(n,locations);
b(m,locations);
}
From previous answers I understand that sequentially executing functions involving jQuery calls can be achieved by using promises (such as .then). However, this solution is only applicable when a(n) returns a promise, which is not achievable under the for-loop structure. Could you please share your insights on how to solve this issue? Thanks in advance for the help.
I would suggest recursion instead of your for loop. For example, you can call the function recursionExample like this,
function a(n) {
return new Promise ((resolve, reject) {
(function recursionExample(a) {
if (a === n) {
resolve;
} else {
$.ajax({ url: 'https://geocoder.cit.api.here.com/6.2/geocode.json',
type: 'GET',
dataType: 'jsonp',
jsonp: 'jsoncallback',
data: {
searchtext: input,
app_id: APP_ID,
app_code: APP_CODE,
},
success: function(data) {
handleData(data);
recursionExample(a + 1);
}
});
}
})(0);
});
}
This will then allow you to use the promise and .then functions. Like so...
function c(n,m) {
var locations = [];
a(n,locations)
.then (function() {
b(m,locations);
});
}

jQuery.when() doesn't seem to be waiting

I need to make a server side call when a user does something in the DOM (click a checkbox, select a dropdown, etc. This is the series of events:
User clicks a checkbox (or something)
A spinner fades in and the UI becomes unavailable
The server side call is made, and gets back some JSON
A label in the UI is updated with a value from the JSON
The spinner fades out and the UI becomes available again
The problem I'm having is that 4 and 5 often get reversed, and the spinner fades out sometimes 2 or 3 seconds before the label is updated.
I'm trying to use .when() to make sure this isn't happening, but I don't seem to be doing it right. I've been looking at this thread, and this one, and jquery's own documentation.
Here's where I'm at right now...
function UpdateCampaign() {
$('#overlay').fadeIn();
$.when(SaveCampaign()).done(function () {
$('#overlay').fadeOut();
});
}
function SaveCampaign() {
var formData =
.... // get some data
$.ajax({
url: '/xxxx/xxxx/SaveCampaign',
type: 'GET',
dataType: 'json',
data: { FormData: formData },
success: function (data) {
var obj = $.parseJSON(data);
.... // update a label, set some hidden inputs, etc.
},
error: function (e) {
console.log(e)
}
});
}
Everything works correctly. The server side method is executed, the correct JSON is returned and parsed, and the label is updated as expected.
I just need that dang spinner to wait and fade out until AFTER the label is updated.
The issue is because you're not giving $.when() a promise. In fact you're giving it nullso it executes immediately. You can solve this by returning the promise that $.ajax provides from your SaveCampaign() function like this:
function SaveCampaign() {
var formData = // get some data
return $.ajax({ // < note the 'return' here
url: '/xxxx/xxxx/SaveCampaign',
type: 'GET',
dataType: 'json',
data: { FormData: formData },
success: function (data) {
var obj = $.parseJSON(data);
// update a label, set some hidden inputs, etc.
},
error: function (e) {
console.log(e)
}
});
}
I know its answered by Rory already. But here's mine promise method, it works fine always and instead of using success and error uses done and fail
var jqXhr = $.ajax({
url: "/someurl",
method: "GET",
data: {
a: "a"
});
//Promise method can be used to bind multiple callbacks
if (someConditionIstrue) {
jqXhr
.done(function(data) {
console.log('when condition is true', data);
})
.fail(function(xhr) {
console.log('error callback for true condition', xhr);
});
} else {
jqXhr.done(function(data){
console.log('when condition is false', data);
})
.fail(function(xhr) {
console.log('error callback for false condition', xhr);
});
}
Or if I want a common callback other than conditional ones, can bind directly on jqXhr variable outside the if-else block.
var jqXhr = $.ajax({
url: "/someurl",
method: "GET",
data: {
a: "a"
});
jqXhr
.done(function(data) {
console.log('common callback', data);
})
.fail(function(xhr) {
console.log('error common back', xhr);
});

How do I know when the last async operation will finish?

I'm building an app in NodeJs and it involves sending external requests asynchronously. Previously I had one id:
# client
function sendAjax(id) {
$.ajax({
type: "POST",
url: "/fsfdsfd",
data: JSON.stringify({"id": id}),
contentType: "application/json; charset=utf-8",
dataType: "json",
}).done(function (data) {
//.....
# server
app.post("/dsfdsfd", function (req, res, nxt) {
var id = req.body.id;
anotherServerClient.sendExternalRequest(id), function(data) {
//success
//return the result, but when exactly?
res.end("ok");
}, function (e) {
// error, but when exactly?
res.end("error");
});
Now I have an array:
# client
function sendAjax(ids) {
$.ajax({
type: "POST",
url: "/fsfdsfd",
data: JSON.stringify({"ids": ids}),
contentType: "application/json; charset=utf-8",
dataType: "json",
}).done(function (data) {
//.....
# server
app.post("/dsfdsfd", function (req, res, nxt) {
var ids = req.body.ids;
for (var id in ids) {
anotherServerClient.sendExternalRequest(id), function(data) {
//success
//return the result
res.end("ok");
}, function (e) {
// error
res.end("error");
});
}
}
How can I know when the last operation in the loop "for (var id in ids) {" will finish to
return the result to the client only after that? What's the idiomatic and simple solition?
// server
app.post("/dsfdsfd", function (req, res, nxt) {
var ids = req.body.ids;
// create output array to collect responses
var output = [];
for (var id in ids) {
anotherServerClient.sendExternalRequest(id, function(data) {
// on success, push the response to the output array
output.push(data);
// check if all responses have come back, and handle send
// if the length of our output is the same as the list of requests
if(output.length >= ids.length){
//return the results array
res.end("ok");
}
}, function (e) {
// if any api call fails, just send an error back immediately
res.end("error");
});
}
});
There are a couple of ways to do this, all idiomatic and down to matter of personal taste.
There is a library called async that provides help in these kind of operations. It specifically contains methods for going through a collection and doing something asynchronous with each of the items. Check out forEachOf, forEachOfSeries, forEachOfLimit for details.
You can achieve this using Promises. Make your API return Promises instead of accepting callbacks. Then create an array of Promises, one for each call and then then wait for all them with Promise.all.
ES6 specification now includes Promises so that is an indication what will be the favorite way in the future.

Wait until Ext.Ajax.request responds with success before setting variable

Below you will see some code to set the currently logged in user for an extjs 4 application. If I have the alert uncommented, the code seems to wait until the alert is accepted before the code continues (it seems). That allows enough time for the asynchronous call to complete with a success. If I comment out the alert, the variable "employeeFilter" never gets set because the AJAX call didn't come back in time. In which case, it sets the "employeeFilter" to null instead. How can I fix this so it waits until the AJAX response comes back in success?
var loggedInUserId = null;
Ext.Ajax.request({
url: '/Controls/UserList/UserService.asmx/GetLoggedInUserId',
method: 'POST',
jsonData: { 'x': 'x' },
success: function (response, opt) {
loggedInUserId = Ext.decode(response.responseText).d;
},
failure: function (response) {
}
});
//alert(loggedInUserId);
var employeeFilter = loggedInUserId;
var projectFilter = '-1';
I would have done this.
var employeeFilter;
Ext.Ajax.request({
url: '/Controls/UserList/UserService.asmx/GetLoggedInUserId',
method: 'POST',
jsonData: { 'x': 'x' },
success: function (response, opt) {
employeeFilter = Ext.decode(response.responseText).d;
//Do here whatever you need to do once the employeeFilter is set. probably call a function and pass the employee filter as parameter.
},
failure: function (response) {
}
});
var projectFilter = '-1';

Categories

Resources