Edits: http://jsfiddle.net/vol7ron/wQZdM/
The fiddle should be used to help illustrate what I'm trying to do and what's happening. The sub-selects should be populated with the second option value.
Original Question:
Not sure the best way to ask. I'm creating a testing script to autofill inputs on a form.
It includes a series of drop-down select boxes, which populate other select options in an onChange event. When trying to auto-populate the form, the sub-selects don't have any options.
console.clear();
// non-select inputs
$(':input:not([type="hidden"],[type="button"])').each(function(){
$(this).val($(this).attr('name')) // test value is simple input's name
});
// select inputs
var count=0, cutoff=7500;
$('select').each(function(){
var t = $(this);
var c = t.children('option');
while( c.length <= 1 && count < cutoff){
count++;
c = $(this).children('option'); // tried not using the cache'd variable
if (!(count % 10))
console.log(count, c.length, "No Options"); // debugging -- never exists early
setTimeout(function(){},0); // not really doing anything
}
t.val( c.eq(1).val() ); // set value to second option value
t.trigger('change'); // calls the onChange even if it doesnt exist
});
// verify it does have data
console.log($('#sub-select').children('option').length); // does have options
There's an AJAX call in the change event. I could modify the callback, but this is just a simple set script for testing, that is run from console. Any ideas?
Not really sure what your code is trying to do
But answering the question How to continue with function after another function call finishes :-
assuming you have a list of functions which are all asynchronous you can nest them to continue
to the next asynchronous function ...
asyncCall(callback1) {
callback1(callback2) {
callback2(...)
}
}
Checkout https://github.com/caolan/async for some elegant ways to do this
this example calls all functions in order even though they are asynchronous.
async.series([
function(callback){
setTimeout(function(){
call_order.push(1);
callback(null, 1);
}, 25);
},
function(callback){
setTimeout(function(){
call_order.push(2);
callback(null, 2);
}, 50);
},
function(callback){
setTimeout(function(){
call_order.push(3);
callback(null, 3,3);
}, 15);
}
1) Use Synchronous AJAX request http://api.jquery.com/jQuery.ajax/
var html = $.ajax({
url: "some.php",
async: false
}).responseText;
2) Instead of using the .each use .eq(index) and just call it in order.
function FakeEach(idx) {
if(idx >= 7500) return;
var $obj = $('select').eq(idx);
if($obj.length == 0) return;
...
$obj.trigger('change');
window.setTimeout(function() { FakeEach(idx++); }, 0);
}
Your problem is that you are starting an AJAX request in the function, and expect that the response arrives before the function ends. As long as you are using an asynchronous request, this will never happen. You have to exit your function before the code that handles the response can run. Javascript is single threaded, so as long as your function is running, no other code can run.
The solution to the problem is to put the code that uses the data in the success callback function that is called after the response has arrived. Eventhough you usually write that function inside the function that makes the AJAX call, it's a separate function that will run later.
If you really need the data in the same function as the AJAX call, you would need to make a synchronous request. This is a killer for the user experience, though, as the entire browser freezes while it is waiting for the response.
Related
i want to stop executing javascript code after the line "return false",but it don't.
my english is poor. if anyone understand me,please improve my question thank you!
$('#category-list input').blur(function(){
var $$ = $(this), $p = $$.prev();
if ($$.val() != $p.html()) {
var data = {'key':$$.attr('name'),'value':$$.val()};
$.post('<?php echo Url::to(['field']); ?>', data).done(function(result){
if (result != 'ok'){
alert(result);
return false; /*i need it stop at here!!!!!! but it not*/
}
});
alert(3);
$p.html($$.val());
}
$$.hide();
$p.show();
});
The short answer is you can't stop a parent function inside an ajax completion function because that will most likely fire after the function itself is finished executing. Regardless, its 2 separate events
$.post is an asynchronous ajax request. This means, that the .done() part of the function will execute AFTER the ajax request comes back.
However, the rest of the function runs synchronously, meaning every (or some) line of code after .post will run before the server even responds. To get around this, you need to re-architect how you execute your functions.
let's examine this with a basic function:
function getSomeAjax() {
//1 - the alert will execute first
alert('first line of execution');
//2 - the post request will execute second
$.post('someUrl', data).done(function(result){
if (result != 'ok'){
alert('post request complete');
//this will execute randomly whenever the server responds.
//it could be in 1 second, 5 seconds, or 20 seconds
}
});
//3
alert('last line of execution');
}
looking at the example above, you should move any logic that is dependent on the server response into the if clause. Otherwise it will execute regardless of your post request
I need to write a setInterval function in javascript. Thi is the code:
var myTimer=setInterval(function(){
var time=0;
$.ajax({
url:'...'
type: "POST",
dataType:"",
success: function (response) {
if(response=="true" || time>=10000){
clearInterval(myTimer);
}
time=time+1000;
},
error: function () {
alert("FAIL");
}
});
},1000);
I don't know why It doesn't stop in clearInterval. Anyone can help me?
You've claimed that the code does "come in the 'if'", so I assume the clearInterval call is actually being made.
Given that, the most likely explanation is that the interval is being cleared (after all, select isn't broken), but before the first "true" response, you've already made more than one ajax call, and the other ones you're seeing are ones scheduled before the interval was cleared.
E.g., your code runs and:
Fires off ajax call #1, which takes more than a second to complete
Fires off ajax call #2
Ajax call #1 completes but isn't "true"
Fires off ajax call #3
Ajax call #2 completes and is "true", clearing the interval
Ajax call #3 completes
Mixing two separate asynchronous intervals (one via setInterval and one via ajax) is asking for trouble.
If the goal is to make the request once a second and stop when you get back "true", I would have the success handler schedule the next call, e.g.:
(function() {
var time = 0;
var started = 0;
start();
function start() {
started = Date.now();
$.ajax({
url: '...'
type: "POST",
dataType: "",
success: function(response) {
if (response != "true") {
// Schedule the next call to occur one second after we
// started the previous call; or almost immediately if
// the call took more than a second to complete
setTimeout(start, Math.max(0, 1000 - (Date.now() - started)));
}
time = time + 1000;
},
error: function() {
alert("FAIL");
}
});
}
})();
Let me illustrate the expected and the actual scenarios to make things clearer.
Scenario #1
The image below shows the case where all your ajax requests complete before one second. You will notice that ajax callback success (or error) functions will execute only before clearInterval (which is what you always expect).
Scenario #2
When some of your ajax requests take more than one second (which is probably what happens), then your ajax callbacks can fire before / after / before-and-after the clearInterval, which makes you feel that your setInterval doesn't stop.
Note that your time variable is useless because it's a function-scoped variable that you initialize to 0 every function call. And even if it's a global variable, it'll only clear the interval in the 11th success function callback, and nothing guarantees how long these 11 successful requests will take.
Solution
As T.J. Crowder suggested, it's better to schedule the next ajax call in the success callback of the previous one, which guarantees that your ajax requests fire sequentially (only one at a time).
Note: Because you edited your question after his answer, then you'll also need to edit the if condition like this:
success: function(response) {
if (response != "true" && time < 10000) {
setTimeout(start, Math.max(0, 1000 - (Date.now() - started)));
}
}
I have an ajax call that retrieves data and on the success of it, runs a loop and runs functions that run more ajax calls.
CODE:
success: function(data){
// FIRST MAKE SURE DATA WAS FOUND
console.log(data);
if(data["status"] == "found")
{
// CREATE NEEDED ARRAY FROM SINGLE STRING
var restrictionArray = data["data_retrieved"].split(',');
loop_amount = restrictionArray.length; //<!-- AMOUNT TO BE LOOPED FOR BUILDING FORMS
//var Action = this.Elevation.getActionsByOptionId(Option.getID())[i];
for(var j = 0; j < loop_amount; j++)
{
var EditRowRestriction = OptionRules.prototype.getEditRowRestriction(j);
var check = $(EditRowRestriction).find("select");
console.log(check[0]);
var EditRowRestirction_select_ability = OptionRules.prototype.getEditRowRestriction_select_ability(j);
//var EditRowRestirction_access_ability = OptionRules.prototype.getEditRowRestriction_access_ability(j);
EditRowRestriction.onremove = function()
{
$(this).next().empty(); <!-- RESTRICTION SELECT ABILITY REMOVE
//$(this).next().next().empty(); <!-- RESTRICTION ACCESS ABILITY REMOVE
$(this).empty();
//var Action = this.Action;
//that.removeAction(Action);
}
tbody.appendChild(EditRowRestriction);
tbody.appendChild(EditRowRestirction_select_ability);
console.log(check[1]);
}
}
},
error:function(){
alert("An error occured, please try again.");
}
Heres the problem, inside the for loop; those methods link to another method that invokes an ajax call. What happens here is even before the ajax call's finish, the loop is always continuing and those methods are always being called. What I want to do is to stop the loop until those methods have returned based on the ajax call's being finished. And than to invoke the last 2 lines of code within the loop:
tbody.appendChild(EditRowRestriction);
tbody.appendChild(EditRowRestirction_select_ability);
What would be my best approach to accomplishing this?
Suggestions, thoughts?
It would be best to consolidate all of this looping with a single server-side script, however, if that isn't an option, you can use .then:
var def = $.Deferred(function(def){
def.resolve();
}).promise(); // used to start .then chain
for (var i = 0; i < 10; i++) {
def = def.then(function () {
return $.ajax({});
});
}
def.done(function(){
// All requests from chain are done
console.log('all done');
});
http://jsfiddle.net/kg45U/
You could modify the calls to $.ajax within the loop to execute synchronously, rather than asynchronously. (See SO question How can I get jQuery to perform a synchronous, rather than asynchronous, AJAX request?.) This would have the effect of "pausing" the loop while the inner ajax calls execute. This would be the most straightforward approach, but does have the disadvantage of locking up the user's browser while those ajax requests execute.
The alternative is to break up the code with the loops into pieces, so that you complete the processing with the loop in a callback function that is invoked after the inner-ajax calls have completed.
I frequently need to load a function on a webpage after between two and five modest-sized data files have loaded. Let's say a maximum of 3MB of data split over a maximum of five files.
I try to optimize the load time by making all the AJAX calls at the same time and loading an initialize() function after they have all loaded, like so:
var data1, data2;
$(document).ajaxStop(function() {
$(this).unbind("ajaxStop"); //prevent running again when other calls finish
initialize();
});
$.ajax({
url: url_to_data_1,
success: function (d) {
data1 = d;
}
});
$.ajax({
url: url_to_data_2,
success: function (d) {
data2 = d;
}
});
function initialize() { /* do something with data1 and data2 */ }
But I'm tired of pasting this in every time, so I want a function like this:
function multi_Ajax(urls, callback) {
var data = {};
//call init() after both the list of prayers and the word concordance index load
$(document).ajaxStop(function() {
$(this).unbind("ajaxStop"); //prevent running again when other calls finish
callback(data);
});
for (var c = 0; c < urls.length; c += 1) {
//data files
$.ajax({
url: urls[c],
dataType: "json",
success: function (d) {
data[urls[c]] = d; console.log("Loaded " + urls[c]);
}
});
}
}
This does not work, of course, since the ajax calls do not exist for ajaxStop to catch. But I do not understand how ajaxStop works well enough to get much further. Thank you!
I'm not sure why your second attempt wouldn't work. According to the documentation, whenever an ajax request completes, jquery will check if there are any other requests that are still outstanding and fires ajaxStop if there are none. Calling $.ajax in a loop shouldn't be any different than hardcoding each call, as far as I can tell.
However, as Barmar suggested in the comments, $.when seems like a cleaner way to do what you want to do. Here's an example of how to pass an array (which you can populate in a loop) to $.when: Pass in an array of Deferreds to $.when()
Using $.when seems cleaner than $.ajaxStop because if someone later comes along and adds an unrelated ajax request somewhere before or after your loop, that would interfere with when ajaxStop triggers. $.when allows you to explicitly say which ajax requests you want to wait for.
EDIT: Here's a fiddle showing ajaxStop working for multiple calls issued in a loop: http://jsfiddle.net/zYk5W/
It looks like the reason this wasn't working for you has nothing to do with .ajax or .ajaxStop; instead it's a scope issue in your success callback. Your success callback closes over c, but this is the same c that the outer (loop) scope uses. By the time any of the success callbacks runs, the for loop has completed and c has been incremented to urls.length. Thus every time your success callback runs, urls[c] is undefined. See the fiddle I linked or JavaScript closure inside loops – simple practical example for an example of how to give each success callback its own c in a scope separate from the loop's c.
I've got a particular function I want to run once, and only after the completion of several AJAX requests.
My current solution looks a bit like this:
function doWork() {
//This is the function to be run once after all the requests
}
//some tracking/counting variables
var ajaxDoneCounter = 0;
var numOfAjaxRequests = 5;
var workDone = false;
function doWorkTrigger() {
ajaxDoneCounter++;
if( !workDone && ajaxDoneCounter >= numOfAjaxRequests ) {
workDone = true;
doWork();
}
}
// ...
//and a number of ajax requests (some hidden within functions, etc)
//they look something like this:
$.ajax({
url: "http://www.example.com",
dataType: "json",
success: function( data ) {
//load data in to variables, etc
doWorkTrigger();
}
});
One obvious pitfall in the above is that any AJAX call that is not successful will not increment ajaxDoneCount and so doWork() will probably never be called. I can get around that using the error callback in inside any $.ajax, so that doesn't worry me too much.
What I want to know is whether the above is safe and/or good practice?
Is there a trick I've missed, or any thing else that might work better?
Update: Since jQuery 1.5, deferred objects [docs] provide a cleaner solution. Have a look at an example here.
I would use .ajaxComplete(), it will be triggered whenever an Ajax call completed (success or error):
var numOfAjaxRequests = 5;
$(document).ajaxComplete(function() {
numOfAjaxRequests--;
if(!numOfAjaxRequests) {
doWork();
}
});
Then you don't have to edit every Ajax request.
You could even use .ajaxSend() to get notified of starting Ajax requests, instead of hardcoding it (but I am not sure whether this really works, maybe you will experience race conditions):
var numOfAjaxRequests = 0;
$(document).ajaxSend(function() {
numOfAjaxRequests++;
});
I think you should use complete(XMLHttpRequest, textStatus) ajax event instead of success(data, textStatus, XMLHttpRequest).
According to jQuery help:
complete(XMLHttpRequest, textStatus)
A function to be called when the
request finishes (after success and
error callbacks are executed). The
function gets passed two arguments:
The XMLHttpRequest object and a string
describing the status of the request.
This is an Ajax Event.
I don't know enough about JavaScript internals, but there is a danger that the operation:
ajaxDoneCounter++;
is not atomic. If that is the case, then this could be subject to a race condition.