I have an issue when trying to get Javascript to execute functions in my desired order. I'm trying to get a jQuery modal form to load information based on a certain selection. I have two SELECT boxes that need to be loaded, but the contents of the second SELECT box depend entirely on the selected value of the first SELECT box.
I made the following functions to request the information I need:
function get_Subjects(varID, callback){
$.post("../vars/get_SID.php", { vid : varID },
function(result){
getInfo('tbsubjectdiv', '../vars/findSubjectlist.php?sid='+result);
});
callback();
}
function get_Selectedfields(varID, callback){
$.post("../vars/requestTblock.php", { vid : varID },
function(result){
populateForm('tbWiz', result);
document.form_tbWiz.varname.disabled = true;
$('.trSearch').hide();
$('.trValueset').hide();
});
callback();
}
function get_TextblockType(varID, callback){
$.post("../vars/requestVtype.php", { vid : varID },
function(result){
if(result == 0){ //Opzoeken
$('.trSearch').show();
}else if(result == 1){ //Datum vergelijken
$('.trSearch').show();
$('.trValueset').show();
}else if(result == 2){ //Percentage
//
}
});
callback();
}
The first function checks the MySQL database for the selected value
of the FIRST SELECT field, and loads the results into the second
SELECT field.
The second function requests the rest of the rest of the form data, and populates the form using populateForm(). It also hides
certain parts of my form in preparation for function three.
The third function basically requests which parts of the form have to be displayed, because that's not always the same.
The whole idea behind this is that I want to use populateForm() to populate all of the form fields. In order for populateForm() to properly set the selected SELECT option, the particular SELECT field must first contain the OPTION it needs to select. Makes sense. I try to make sure of this with my first function, which will load all of the OPTIONs. THEN I try to use the get_Selectedfields() to populate all the proper values. This is not what happens though. No matter what I try to do, getInfo() in the first function is ALWAYS being called LAST. This makes it impossible for populateForm() to select the proper option, which is driving me mad.
I'm trying to "force" the execution-order by doing this:
function getTextblock(var_ID){
get_Subjects(var_ID, function() {
get_Selectedfields(var_ID, function() {
get_Textblocktype(var_ID, function() {
// Done
});
});
});
}
When I realised it still did not work the way I wanted, I decided to use Chrome's Developer Tools to check the order in which everything is executed. It all works as expected, but at the very end it jumps straight back to getInfo(), which is part of the FIRST function I called. I'm absolutely clueless as to why getInfo() gets executed last. If this just gets executed at the very beginning, where I want it to execute, it would all work fine.
You have to call the callback in the callback function of the post request:
function get_Subjects(varID, callback){
$.post("../vars/get_SID.php", { vid : varID },
function(result){
getInfo('tbsubjectdiv', '../vars/findSubjectlist.php?sid='+result);
callback();
});
}
function get_Selectedfields(varID, callback){
$.post("../vars/requestTblock.php", { vid : varID },
function(result){
populateForm('tbWiz', result);
document.form_tbWiz.varname.disabled = true;
$('.trSearch').hide();
$('.trValueset').hide();
callback();
});
}
function get_TextblockType(varID, callback){
$.post("../vars/requestVtype.php", { vid : varID },
function(result){
if(result == 0){ //Opzoeken
$('.trSearch').show();
}else if(result == 1){ //Datum vergelijken
$('.trSearch').show();
$('.trValueset').show();
}else if(result == 2){ //Percentage
//
}
callback();
});
}
The POST is being handled asynchronously in your functions so your "callback" is really just being executed almost immediately after your initial call, whereas the callback of $.post is being executed after the post has occurred. Does that help you sort things out? You will probably need to kick off the rest of the process in the callback of $.post("../vars/get_SID.php", { vid : varID }...
$.post is shorthand for $.ajax so you can read up a bit more in the jQuery docs, but I would not suggest switching to synchronous requests. If you absolutely must have one request finished before the next can execute then kicking off that next step from the callback is the way to go.
You're using ajax. The first a is for asynchronous. If you called the functions from the function(result) blocks then they would occur in order.
Alternatively (and this isn't a great idea but you can do it) use the $.ajax() object and set async to false.
As you don't know how long long an ajax request will actually take, you can only chain events within the ajax response:
function getTextblock(var_ID){
$.post(YOUR_TARGET, YOUR_DATA, function(result){
YOUR_CODE
// CHAIN HERE, call new function or sub ajax request
});
}
Wesley,
The javascript will execute always on the predefined order. If you put a bunch of "alerts()" in the middle of your code, you can taste that.
But this is not true for callbacks, because they will be moved to the bottom of execution stack on javascript where we can't determine the order, since they are called by a AJAX return which by definition is asynchronous.
Even though your ajax executes in a millisecond, the callback will not be executed until all methods in your script block have finished.
You have, actually three options:
Chain all the methods sequence in callbacks. Please, don't call a callback!! It inst supposed to be you, but the "system" that will call those.
// The data you need first
function myStartPoint() {
$.post(url, function(result) {
// do what you need with this result (this is your callback, but anonymous)
// then, call the next step
secondPoint();
});
}
function secondPoint() {
$.post(url, function(result) {
// again, the callback is anonymous... your hardly need to declare something named callback
// chain how many points as you need
nextPoint();
}
}
"Force" the ajax to be synchronous with async:false option. This can cause performance issues.
The ugliest of all is to use the damned setTimeout which is very, very wrong, but will work in your case because, the setTimeout will put the method on the bottom of execution stack even after those callbacks which are expected to be fast. Seriously, I just put this option because eventually someone would say it... Do not take this path.
Related
I am using jquery and php along with WordPress to access data from a mysql database.
I managed to retrieve that data but would like jquery to wait until all the data has been retrieved.
Below is the pertinent code segment.
Both alert commands display nothing because the data has not finished loading.
How can I re-code this?
jQuery(document).ready(function ($) {
load_qs('foo');
alert($(".all-qa-free").val());
//////////////////////////////////////////////////////////////////////////
function load_qs(data) {
var data = {
action: 'load_question_set',
async: false,
cache: false,
qset_id: data
};
$.post(the_ajax_script.ajaxurl, data, function (response) {
var mydb_data = $.parseJSON(response);
$("#all-qa").val(mydb_data.qa);
alert($(".all-qa-free").val());
return;
});
}
});
Ajax calls are asynchronous, while the JS executes synchronously. jQuery/JavaScript initiates the ajax calls and immediately goes to the next line for executing. It doesn't wait for the response to come from the server.
Solution:
Do all the processing, which you want to do on server response in the callback function itself. In your case, in the below function:
function(response) {
var mydb_data = $.parseJSON(response);
$("#all-qa").val(mydb_data.qa);
alert ($(".all-qa-free").val());
return;
}
Otherwise you will keep facing this issue. Try putting alert in the callback function. It'll definitely work.
One more thing, you are putting value in id "#all-qa" and fetching data from ".all-qa-free", please check if you are doing this right.
--
HappyCoding
You should set async: true in your ajax call.
Setting async to false means that the statement you are calling has to complete before the next statement in your function can be called.
If you set async: true then that statement will begin it's execution and next statement will be called regardless of whether the async statement has completed yet.
I'm creating online chat, but I'm wondering while using jQuery .load() in my script, my browser seems to get slow. When i checked the inspect element "Net" section, it loads bunches of GET-data... etc.
I would like to know if there's a better script solution with this code to prevent chat being heavy in the background while the data keeps looping in the background to check who's keep coming online/offline.
setInterval('loadThis()', 5000);
function loadThis () {
$("#loads").load('includes/users.php', function(){
$(".chat-side-panel li").each(function(i){
i = i+1;
$(this).addClass("stats"+i);
var status = $(".stats"+i).find("span.bullet").data("status"),
flag = $(".stats"+i).find("span.mail").data("flag");
if(status == 1) {
$(".stats"+i).find("span.bullet").addClass("online");
}
if(flag == 1) {
$(".stats"+i).find("span.mail").addClass("active");
}
});
});
}
the Chat-Side-Panel will be the main panel, and LI will be the listings of users including their status (online/offline) and flag (message received). As for the standard, what can you suggest for the setInterval time loading (if 5sec. is enough) or should i increase it.
Thanks for your input for this.
PS. We're doing this with both PHP/MySQL also.
One issue I see is that you keep re-querying the DOM for the same elements. Get them once, re-use them thereafter:
var load_target = $('#loads');
function loadThis () {
load_target.load('includes/users.php', function () {
load_target.find('.chat-side-panel li').each(function (i) {
var stats_li = $(this),
bullet = stats_li.find('span.bullet'),
mail = stats_li.find('span.mail');
bullet.toggleClass('online', (bullet.data('status') == 1))
mail.toggleClass('active', (mail.data('flag') == 1));
});
});
}
I don't know all of your involved logic or what the rest of your system looks like, so this particular code may not work exactly. It should simply serve as a re-factor done in a vacuum to show what that function could look like if you stopped hitting the DOM so hard.
Also, use of setInterval is not generally recommended. If the load of the remote file takes a while, you could end up calling loadThis() again before a previous one was completed. This would compound your DOM issues if calls to loadThis() began stacking up. Recursive use of setTimeout is preferred in a situation like this. Here is the above code modified to run recursively, and some usage examples below that:
var load_target = $('#loads'),
loadThis = function (start_cycle) {
$.ajax({
url: 'includes/users.php',
dataType: 'html',
type: 'GET',
success: function (response) {
load_target
.html(response)
.find('.chat-side-panel li').each(function (i) {
var stats_li = $(this),
bullet = stats_li.find('span.bullet'),
mail = stats_li.find('span.mail');
bullet.toggleClass('online', (bullet.data('status') == 1))
mail.toggleClass('active', (mail.data('flag') == 1));
});
},
complete: function () {
if (typeof start_cycle !== 'boolean' || start_cycle) {
load_target.data('cycle_timer', setTimeout(loadThis, 5000));
}
}
});
};
//to run once without cycling, call:
loadThis(false);
//to run and start cycling every 5 seconds
loadThis(true);
// OR, since start_cycle is assumed true
loadThis();
//to stop cycling, you would clear the stored timer
clearTimeout(load_target.data('cycle_timer'));
Last years (around 2012) I developed a chat system for a social network, and saw that
Using setInterval issue is when the request is being sent regularly, without waiting or carry about the result of the first requests in the queue. Sometimes the script can not respond and Mozilla or IE asks the user whether he should block or wait for the non-responding script.
I finally decided to use setTimeout instead. Here is what I did (I use $.getJSON so please study the example and how can use load instead)
function loadThis () {
$.getJSON('url').done(function(results){
//--use the results here
//then send another request
setTimeOut(function(){
loadThis();
},5000);
}).fail(function(err){
//console.log(print(err))
setTimeOut(function(){
loadThis();
},1000);
});
}
loadThis();
PS.: I would like to mention that the time depends on our many items are to be retrieved in your users.php file. Maybe you should use the paging tip. Your users.php can then treat url params users.php?page=1&count=100 for the first request, users.php?page=2&count=100 for the second until the results rows number is 0.
EDITS: In addition, I suggest you consider not interacting with the DOM every time. It is important too.
I'm working on a PHP / AJAX application and it's quickly becoming unmanageable!
The application is designed to work much like a a desktop application so almost every user action results in an AJAX call.
For every one of these actions I have some jQuery that posts the data to my PHP script and runs a corresponding PHP function that handles the server side actions.
That means in my jQuery file i'll have something like this:
$('.delete-project').on('click', function(){
// Ajax request to http://myapp.co.uk/ajax/delete_project
});
$('.delete-user').on('click', function(){
// Ajax request to http://myapp.co.uk/ajax/delete_user
});
$('.delete-keyword').on('click', function(){
// Ajax request to http://myapp.co.uk/ajax/delete_keyword
});
I'm sure there is a better way of doing things, but how is it generally done to avoid lots of similar code? The above actions could possible rolled into one 'delete' ajax request which posts the item type and a database ID but a lot of my functions post different data and require different parameters so wouldn't fit so neatly under one jQuery handler.
I've tried finding some resources on how an AJAX application should be put together but all I can find is beginner tutorials on making AJAX requests etc, not how to write a scalable AJAX application.
Just to be clear I know how AJAX works, I'm just trying to find the best way of implementing it in terms of reducing the jQuery and PHP needed where possible.
Are there any good resources that deal with this sort of thing?
You can roll all those into one delete function by using attributes in HTML, for example:
$('.delete').on('click', function(){
var delete = $(this).attr('data-delete');
// Ajax request to http://myapp.co.uk/ajax/delete_{delete}
});
Then your HTML would be something like:
Delete
More information on data-attributes
Not sure if this would help but you could create some functions to reduce your code as it grows. For starters you could prevent duplicating of the ajax call by putting the jQuery .ajax function in a custom wrapper function of your own. For example:
function ajaxGet(myUrl, queryString, successCallback, errorCallback) {
if(queryString) myUrl+= "?" + queryString;
$.ajax({
url: myUrl,
type: "GET",
data: null,
success: function (res) {
if(!successCallback) return;
else successCallback(res);
},
failure: function (res) {
if(!errorCallback) return;
else errorCallback(res);
}
},
By creating a wrapper function, you can pass in the needed data without duplicating the $.ajax call code over and over in each of the click functions. You can also create a similar function for an ajax call using post. You could then dynamically build the click functions to further reduce your code:
function buildClicks() {
setupClick([url], [data], [success], [error], [$(elem)]);
setupClick([url], [data], [success], [error], [$(elem)]);
setupClick([url], [data], [success], [error], [$(elem)]);
}
//Setup clicks for each button or link
function setupClick(url, data, success, error, elem) {
elem.click(function () {
ajaxGet(url, data, success, error);
});
}
In this example I'm assuming your using a "GET" and adding a query string. You could easily adapt this to pass, a JSON formatted object for example, using a custom "POST" function. In that case [data] would be an object not a query string.
Sorry this code isn't the clearest. Let me explain it a little more. The buildClicks function would allow you to setup multiple click events on different elements by passing in the required data. I'm not sure if you are passing any data, but the above functions would allow for it. By dynamically creating the clicks you can avoid duplicating the .click code over and over. Just call the buildClicks function on document ready as such.
$(document).ready(function () { buildClicks(); });
NOTE: The success and error callbacks are functions that will be executed when your call either completes successfully or errors out. If you do not wish to use these they can be ommitted or null can be passed in. Make sure if you do pass in functions that you leave off the "()" on the end of the function name. Otherwise the functions will be executed prior to the success or failure of the ajax call. For example:
ajaxGet("http://testurl.com", null, ajaxSuccess, ajaxFailure);
ajaxSuccess() {
}
ajaxFailure() {
}
You could set an ID attributes to that buttons and a class like submit-button.
Then hang a handler on that $('.submit-button') while using an ID attribute value to define the URL to call, like that:
$('.submit-button').on('click', function(){
$action = $(this).attr('id'); //lets say the ID's value is 'delete_project'
$url = 'http://myapp.co.uk/ajax/' + $action;
// Ajax request to http://myapp.co.uk/ajax/delete_project is done
});
Anyway any web application that tends to be like a desktop one is only a bunch of JS and few HTML with some PHP behind... That is always badly maintanable...
I used [extJs][1] once for this, which led to using only jQuery and their modules while no (or just a minimum of) HTML was needed to write...
Not a copy-and-paste-solution, but maybe you find some more ideas how to build a scalable client-server application when looking for how REST APIs are built. I find REST a very good structure to keep a clear server-side API when building such apps, and i'm sure there are tutorials around how to do the corresponding client-part cleanly too.
In my application I do quite a lot of ajax calls too. I found that the easiest way to do things was to create myself a wrapper for all ajax calls such as:
var MySite = function()
{
AjaxWrapper : function(type,url,data,callback,noloading)
{
$.ajax({
type: type,
url: url,
data: data,
success:function(json)
{
if(json.status === true)
{
if(typeof callback === 'function')
{
callback(json);
} else {
// A generic success handler
}
} else {
// An error handler
}
}
});
};
}();
then you'd call it like:
MySite.AjaxWrapper("GET", "somehref", {}, function(json)
{
// json has the result of your json callback
// you could also make this a separate function
// or not have it
});
this let's you call your ajax on just about anything you want. You could then use something like data attributes, or just the standard href for anything that requires ajax and add an event to all links that pass through this function. Or, if you wanted some to do certain things just make the callback function different for those.
I'm finding this hard to explain, but this is what I've used for a couple of ajax-rich projects and it makes things so much easier!
For an example for your case you could then use something like:
$('a[class|="delete"]').on("click", function()
{
MySite.AjaxWrapper("POST", $(this).attr("href"), {param:number1}, DeleteHandler);
});
The way I would do this would be to have a single function for all actions:
$('.actions').on('click', function (e) {
e.preventDefault();
var action = $(this).data('action'),
id = $(this).data('id');
$.get('/local/handler.php', {
'action': action,
'id': id
}, function () {
// Callback stuff here
});
});
And HTML:
Delete Project
Delete User
Delete Keywork
And the PHP file should have if statements based on the action parameter that performs the requested action.
EDIT:
This way your not limited to just delete actions, so you can scale your app in the future.
Also, If the different actions become a large list (e.g. deletes of many kinds, updates, adding), I would create a separate PHP file for each action and include them in one master file. This will allow for easy scaling.
I am using jQuery for my Ajax calls... I have x amount of Ajax calls that append to a div. These Ajax load requests are generated by a PHP foreach loop... The problem is they render out of the order; they are set in the array...
<script type="text/javascript">
function loadPage(target, url, append)
{
if (append == true) {
$.get(url, function(data) { $(target).append(data) });
}
else {
$(target).load(url);
}
return false;
}
</script>
////// ----- PHP
<?php
$this->data['sidebar'] = array('login', 'active_leagues', 'latest_forum_threads', 'latest_matches', 'sponsors');
if (isset($sidebar[0]) && !empty($sidebar[0]))
{
echo '<div class="right_col">';
foreach($sidebar as $val)
{
echo "<script>loadPage('.right_col', 'http://dev.banelingnest.com/sidebar/". $val ."', true)</script>";
}
echo '</div>';
}
I am wonder if the cause of this is the web server responding slower to some requests than others... Other than that, I have no clue why this could be happening. Do you have any thoughts how I could keep the requests in order?
You have to create reference points before the requests, and append the results to them:
var counter = 0;
function loadPage(target,url,append)
{
if (append == true) {
var id = "container_"+counter;
$(target).append("<div id='"+id+"'></div>")
$.get(url, function(data) {
$("#"+id).append(data);
});
counter++;
} else {
$(target).load(url);
}
return false;
}
Your reference elements will be appended to the target on every loadPage() call, so they will be in the correct order, and the request can come in any order they will be loaded in their right place.
This is happening because the ajax calls are asynchronous, and the order they go out has nothing to do with the order they are returned. They will all happen independently and it's expected for some to run faster than others.
You will need to use $.ajax instead of $.get, and set async to false.
See this question: How can I get jQuery to perform a synchronous, rather than asynchronous, Ajax request?
You can also use the unique and interesting solution presented by #inti.
You could do synchronous requests instead of asynchronous, which'd force the browser to wait until each individual request finishes before starting the next. The downside that is for any "lengthy" requests (or many short ones), the browser will be locked up.
You may want to investigate sending all your requests in a single AJAX call, rather than doing one-request-per-call. That way it'd be easy for the scripts on both sides to keep everything in order. Otherwise you're stuck depending on the user link to your server having low error rates, low latency, and low congestion.
So instead of doing the equivalent of
loadPage(1); // fetch data #1
loadPage(37); // fetch data #37
loadPage(203); // fetch data #203
do something like
loadPage([1,37,203]); // fetch all 3 at once.
I have 2 ideas that may help, the first is:
jQuery has a $(document).ready(function() function that is possibly being called from a parent function or being inherited somehow, this means the JavaScript won't run before the rest of the PHP has loaded.
I have seen some functions inherit this from jQuery without it being declared.
The second is:
I am assuming that this function is running in the head or early on in your page and not the foot or later on in the document.
I hope they help.
This is the nature of AJAX, and yes the server is responding faster to some than others.
If you want them in order, you would have to make the first call, then on the complete event, call the next one, and so on; in essence creating a synchronous chain of calls (kind of goes against the A in AJAX).
Without knowing your specific reasons for wanting them in order, this may be a lot more work than what it's worth.
However you do it, it will take away from the user experience, because if one call is slow, all of the other will have to wait.
The simplest solution is creating placeholders, as inti described. Your elements will not necessarily appear in order, but they will end up in the right order. If you need them to appear in order too, here is a simple queue using deferreds:
var queue = [];
function loadPage(target,url) {
queue.push($.get(url));
$.when.call($, queue).then(function() {
$(target).append(Array.prototype.pop.call(arguments));
});
}
The AJAX calls will run in parallel, but the callbacks will fire strictly in order.
Here is what I did with the array or urls I needed to load in order.
I created the order of wrappers first, than did the ajax calls, and load the results into the matching wrapper. This keeps the calls asynchronous, but you still the the proper order.
$.fn.dashboarder = function(options)
{
var settings = $.extend({
urls: [],
}, options || {});
var self = this;
if (settings.urls.length)
{
$(self).html('');
/// create wrapper blocks in the proper order, so they eventually display in this order
$(settings.urls).each(function( index, value )
{
var wrapper = $( "<div />" )
.addClass('dashboard-block-item')
.attr('id', 'dashboard-block-item-'+index);
$(self).append($(wrapper));
});
$(settings.urls).each(function( index, value )
{
$('#dashboard-block-item-'+index).load(value, function( response, status, xhr )
{
}).delay(5000 * index);
});
}
return this;
}
function debug( obj ) {
if ( window.console && window.console.log ) {
window.console.log( obj );
}
};
basically i want to hold a parameter that retrieve value from $.post() call like this:
init = function(){
var lastpage = getLastPage();
}
function getLastPage(){
$.post("getInfo.php",{
last: "yes"
},
function(data){
setLast(data.last);
},'json');
return function setLast(data){
return data;
}
}
so when reach at last post (last page) i should check with lastpage variable that has a value returned from getLastPage() function.
I'm pretty blur with javascript pointer and all. Please help guys.
update (20/4/2010):
I've done the other way around, like this:
init = function(){
getLastPage();
if((page+1) == $("#lastpage").val()){
alert("this is last post");
}else{
page++;
//get info and display to the page here
}
}
function getLastPage(){
$.post("getInfo.php",{
last: "yes"
},
function(data){
$("#lastpage").val(data.last);
},'json');
}
first run the function to temporarily store the value in hidden input tag (lastpage) and then grab the value again to check it whenever i click forward button.
if you all have more appropriate way please tell me.
You should change your code around like this:
$.post("getInfo.php",{ last: "yes" },
function(data){
functionToRunAfterYouHaveDataSometimeLater(data.last);
}
,'json');
The problem with your overall approach is that with AJAX, you're dealing with an asynchronous operation. This means that the function(data) { } portion doesn't run then, it runs later, so your return doesn't actually return anything, it'll be undefined.
Instead of this approach, you need to call $.post() then call whatever function relies on this data to continue as part of $.post()'s callback. After doing that your code order looks like this:
$.post() executes, firing off a request to the server
The rest of your code after $.post() runs
Later when the response comes from the server and you have data, your callback executes
Now continue to do what you need with that data