Given: a php-script for parsing portions of data on a web-site. It parses about 10k products hence rather slow.
I need to make a web-frontend with html/css/js for it. I made a loop which makes ajax-requests and shows progress inforamtion. It uses syncronous ajax because it needs to wait until another request is done to perform another.
do {
var parseProductsActive = true;
var counter = 0;
myAjax('parseProducts.php?start='+counter, false, function(resp) {
if (resp[0]=='s') {
counter += Number(resp.substring(1));
parseProductsActive = false;
}
else {
counter += Number(resp);
}
self.postMessage(counter);
});
} while (parseProductsActive==true);
I'm doing it in a Web Worker because I'm afraid it's going to hang up the interface because of this endless loop and (a)synchronousness of ajax itself won't help to solve the prolem.
But when I tried to use ajax in a web worker I found it's hard though possible because jQuery doesn't work in a Web Worker at all. It uses DOM even for non-DOM operations and DOM isn't available in a Web Worker. And many developers doubt using Web Workers at all. I just wanted to ask if I am doing it right or wrong. Is there any more surface solutions to that I can't see?
You guessed right: a recursive callback is the way to do a bunch of asynchronous requests in sequence. It might look a bit like this:
var parseProductsActive = true;
var counter = 0;
//define the loop
function doNextAjax(allDone){
//Instead of just returning, an async function needs to
//call the code that comes after it explicitly. Receiving a callback
//lets use not hardcode what comes after the loop.
if(!parseProductsActive){
allDone();
}else{
//use async Ajax:
myAjax('parseProducts.php?start='+counter, true, function(resp) {
if (resp[0]=='s') {
counter += Number(resp.substring(1));
parseProductsActive = false;
}
else {
counter += Number(resp);
}
self.postMessage(counter);
doNextAjax(); // <---
});
}
//Start the loop
doNextAjax(function(){
console.log("the code that runs after the loop goes here")
});
//BTW, you might be able to get rid of the "parseProductsActive" flag with a small
// refactoring but I'm keeping the code as similar as possible for now.
//It would be kind of equivalent to writing your original loop using a break statement.
Yes, its ugly and verbose but ints the only way to do it in raw Javascript. If you want to write a more structured version that looks like a loop instead of something with tons of gotos, have a look at one of the async control flow libraries or one of the compilers that compiles extensions of Javaascript with async support back into regular JS with callbacks.
I am trying to make a basic enough page that allows the user to execute a php script by clicking a button. Each button will have a loading spinner popup on clicking.
My problem is, on clicking one button and then clicking another, both spinners close at the exact same time even though the second may still be processing.
Does anyone know how to make these spinners truly asynchronous ? Thanks so much in advance, its killing me.
JS:
function test(element){
var append = "#";
var test = append.concat(element);
document.getElementById(element).style.visibility='visible';
$.ajax({url:"test.php",success:function(result){
hide(element);
}
});
};
function hide(element){
document.getElementById(element).style.visibility='hidden';
};
</script>
HTML:
<html>
<?
$index = 0;
$myArray = array ("1", "2", "3", "4", "5");
for($index = 0; $index < 5; $index++){?>
<button onclick="test('<?echo $myArray [$index];?>')">Start</button>
<img id="<?echo $myArray [$index];?>" src="images/loader.gif"
style="visibility:hidden"/>
<br><br>
<?}?>
</html>
I would implement a counter. Each time you show the loading indicator, add one to the counter and each time you want to hide it, subtract one. Then monitor the counter and whenever it is above zero show the loading indicator and when at zero hide it. Make sense?
Something like the following (untested) code might do the trick and it neatly means you can avoid worrying about the spinner at all in ajax requests:
var spinningAjax = (function() { // use of the closure created by an immediate function gives us the scope to create a persistant counter variable
var counter = 0;
$(document).ajaxComplete(function() {
counter--;
if (counter === 0) {
showSpinner(false);
}
});
return function(settings) {
counter++;
showSpinner(true);
$.ajax(settings);
}
})();
var showSpinner(bool) {
// I'll leave this up to you as it looks like your posted html / js is for example purposes rather than replicating your actual site
};
EDIT: Ok, having seen the comments to another answer, I realise this doesn't quite solve the situation you're in. I'll have a think and see if I can do better
EDIT2: I think this (still untested, unfortunately) code may be what you require. Please let me know in the comments if you have any issues.
var spinningAjax = (function() { // closure of immediate function lets us create a persistant array of the counters for each spinner
var counter = []; // an array to hold the counters for each spinner
$(document).ajaxComplete(function(event, xhr, settings) { // called whenever any ajax request is completed
if (typeof settings.ajaxGroup !== 'undefined') { // only update the counters if an ajaxGroup has been provided
counter[settings.ajaxGroup]--;
if (counter[settings.ajaxGroup] === 0) {
showSpinner(false, settings.ajaxGroup); // hide spinner when all requests connected with the spinner have been completed
}
}
});
return function(settings) { // this is the function actually assigned to the variable spinningAjax as a result of the immediate function
counter[settings.ajaxGroup] = counter[settings.ajaxGroup] ? counter[settings.ajaxGroup]+1 : 1; // can't just use the ++ operator as this property might not be defined yet
showSpinner(true, settings.ajaxGroup);
$.ajax(settings);
}
})();
var showSpinner(bool, spinnerIdentifier) {
// I'll leave this up to you as it looks like your posted html / js is for example purposes rather than replicating your actual site
};
I am trying to call showUpload(); from within two setTimeouts. Neither works. It seems to be out of scope and I'm not sure why. I tried this.showUpload() which didn't work either.
$(document).ready(function(){
var progress_key = $('#progress_key').val();
// this sets up the progress bar
$('#uploadform').submit(function() {
setTimeout("showUpload()",1500);
$("#progressbar").progressbar({ value:0}).fadeIn();
});
// uses ajax to poll the uploadprogress.php page with the id
// deserializes the json string, and computes the percentage (integer)
// update the jQuery progress bar
// sets a timer for the next poll in 750ms
function showUpload() {
$.get("/myid/videos/uploadprogress/" + progress_key, function(data) {
if (!data)
return;
var response;
eval ("response = " + data);
if (!response)
return;
var percentage = Math.floor(100 * parseInt(response['bytes_uploaded']) / parseInt(response['bytes_total']));
$("#progressbar").progressbar({ value:percentage})
});
setTimeout("showUpload()", 750);
}
});
Thank you for your time.
As #Daniel said, this should work:
setTimeout(showUpload, 750);
Please note that the quotes should be removed (this is why it isn't being executed until the timeout runs out). Right now, you are passing a string, which is evaled when the timeout runs out. This eval will happen in a different scope, which is why you are seeing the problem you are seeing.
Instead, passing a reference to the showUpload function to setTimeout will allow your function to be executed later. Keep in mind that when it runs, it will be in a different scope, so you may have other scope issues, like with progress_key. You will need to create a closure around showUpload to capture that parameter.
It looks like you need to remove the parenthesis from showUpload in both your setTimeout calls. Otherwise you will be invoking the showUpload method instead of passing it as a parameter:
setTimeout(showUpload, 750);
I've only found rather complicated answers involving classes, event handlers and callbacks (which seem to me to be a somewhat sledgehammer approach). I think callbacks may be useful but I cant seem to apply these in the simplest context. See this example:
<html>
<head>
<script type="text/javascript">
function myfunction() {
longfunctionfirst();
shortfunctionsecond();
}
function longfunctionfirst() {
setTimeout('alert("first function finished");',3000);
}
function shortfunctionsecond() {
setTimeout('alert("second function finished");',200);
}
</script>
</head>
<body>
Call my function
</body>
</html>
In this, the second function completes before the first function; what is the simplest way (or is there one?) to force the second function to delay execution until the first function is complete?
---Edit---
So that was a rubbish example but thanks to David Hedlund I see with this new example that it is indeed synchronous (along with crashing my browser in the test process!):
<html>
<head>
<script type="text/javascript">
function myfunction() {
longfunctionfirst();
shortfunctionsecond();
}
function longfunctionfirst() {
var j = 10000;
for (var i=0; i<j; i++) {
document.body.innerHTML += i;
}
alert("first function finished");
}
function shortfunctionsecond() {
var j = 10;
for (var i=0; i<j; i++) {
document.body.innerHTML += i;
}
alert("second function finished");
}
</script>
</head>
<body>
Call my function
</body>
</html>
As my ACTUAL issue was with jQuery and IE I will have to post a separate question about that if I can't get anywhere myself!
Well, setTimeout, per its definition, will not hold up the thread. This is desirable, because if it did, it'd freeze the entire UI for the time it was waiting. if you really need to use setTimeout, then you should be using callback functions:
function myfunction() {
longfunctionfirst(shortfunctionsecond);
}
function longfunctionfirst(callback) {
setTimeout(function() {
alert('first function finished');
if(typeof callback == 'function')
callback();
}, 3000);
};
function shortfunctionsecond() {
setTimeout('alert("second function finished");', 200);
};
If you are not using setTimeout, but are just having functions that execute for very long, and were using setTimeout to simulate that, then your functions would actually be synchronous, and you would not have this problem at all. It should be noted, though, that AJAX requests are asynchronous, and will, just as setTimeout, not hold up the UI thread until it has finished. With AJAX, as with setTimeout, you'll have to work with callbacks.
I am back to this questions after all this time because it took me that long to find what I think is a clean solution :
The only way to force a javascript sequential execution that I know of is to use promises.
There are exhaustive explications of promises at : Promises/A and Promises/A+
The only library implementing promises I know is jquery so here is how I would solve the question using jquery promises :
<html>
<head>
<script src="http://code.jquery.com/jquery-1.9.1.min.js"></script>
<script type="text/javascript">
function myfunction()
{
promise = longfunctionfirst().then(shortfunctionsecond);
}
function longfunctionfirst()
{
d = new $.Deferred();
setTimeout('alert("first function finished");d.resolve()',3000);
return d.promise()
}
function shortfunctionsecond()
{
d = new $.Deferred();
setTimeout('alert("second function finished");d.resolve()',200);
return d.promise()
}
</script>
</head>
<body>
Call my function
</body>
</html>
By implementing a promise and chaining the functions with .then() you ensure that the second function will be executed only after the first one has executed
It is the command d.resolve() in longfunctionfirst() that give the signal to start the next function.
Technically the shortfunctionsecond() does not need to create a deferred and return a promise, but I fell in love with promises and tend to implement everything with promises, sorry.
I am an old hand at programming and came back recently to my old passion and am struggling to fit in this Object oriented, event driven bright new world and while i see the advantages of the non sequential behavior of Javascript there are time where it really get in the way of simplicity and reusability.
A simple example I have worked on was to take a photo (Mobile phone programmed in javascript, HTML, phonegap, ...), resize it and upload it on a web site.
The ideal sequence is :
Take a photo
Load the photo in an img element
Resize the picture (Using Pixastic)
Upload it to a web site
Inform the user on success failure
All this would be a very simple sequential program if we would have each step returning control to the next one when it is finished, but in reality :
Take a photo is async, so the program attempt to load it in the img element before it exist
Load the photo is async so the resize picture start before the img is fully loaded
Resize is async so Upload to the web site start before the Picture is completely resized
Upload to the web site is asyn so the program continue before the photo is completely uploaded.
And btw 4 of the 5 steps involve callback functions.
My solution thus is to nest each step in the previous one and use .onload and other similar stratagems, It look something like this :
takeAPhoto(takeaphotocallback(photo) {
photo.onload = function () {
resizePhoto(photo, resizePhotoCallback(photo) {
uploadPhoto(photo, uploadPhotoCallback(status) {
informUserOnOutcome();
});
});
};
loadPhoto(photo);
});
(I hope I did not make too many mistakes bringing the code to it's essential the real thing is just too distracting)
This is I believe a perfect example where async is no good and sync is good, because contrary to Ui event handling we must have each step finish before the next is executed, but the code is a Russian doll construction, it is confusing and unreadable, the code reusability is difficult to achieve because of all the nesting it is simply difficult to bring to the inner function all the parameters needed without passing them to each container in turn or using evil global variables, and I would have loved that the result of all this code would give me a return code, but the first container will be finished well before the return code will be available.
Now to go back to Tom initial question, what would be the smart, easy to read, easy to reuse solution to what would have been a very simple program 15 years ago using let say C and a dumb electronic board ?
The requirement is in fact so simple that I have the impression that I must be missing a fundamental understanding of Javsascript and modern programming, Surely technology is meant to fuel productivity right ?.
Thanks for your patience
Raymond the Dinosaur ;-)
In your example, the first function does actually complete before the second function is started. setTimeout does not hold execution of the function until the timeout is reached, it will simply start a timer in the background and execute your alert statement after the specified time.
There is no native way of doing a "sleep" in JavaScript. You could write a loop that checks for the time, but that will put a lot of strain on the client. You could also do the Synchronous AJAX call, as emacsian described, but that will put extra load on your server. Your best bet is really to avoid this, which should be simple enough for most cases once you understand how setTimeout works.
I had the same problem, this is my solution:
var functionsToCall = new Array();
function f1() {
$.ajax({
type:"POST",
url: "/some/url",
success: function(data) {
doSomethingWith(data);
//When done, call the next function..
callAFunction("parameter");
}
});
}
function f2() {
/*...*/
callAFunction("parameter2");
}
function f3() {
/*...*/
callAFunction("parameter3");
}
function f4() {
/*...*/
callAFunction("parameter4");
}
function f5() {
/*...*/
callAFunction("parameter5");
}
function f6() {
/*...*/
callAFunction("parameter6");
}
function f7() {
/*...*/
callAFunction("parameter7");
}
function f8() {
/*...*/
callAFunction("parameter8");
}
function f9() {
/*...*/
callAFunction("parameter9");
}
function callAllFunctionsSy(params) {
functionsToCall.push(f1);
functionsToCall.push(f2);
functionsToCall.push(f3);
functionsToCall.push(f4);
functionsToCall.push(f5);
functionsToCall.push(f6);
functionsToCall.push(f7);
functionsToCall.push(f8);
functionsToCall.push(f9);
functionsToCall.reverse();
callAFunction(params);
}
function callAFunction(params) {
if (functionsToCall.length > 0) {
var f=functionsToCall.pop();
f(params);
}
}
If you don't insist on using pure Javascript, you can build a sequential code in Livescript and it looks pretty good. You might want to take a look at this example:
# application
do
i = 3
console.log td!, "start"
<- :lo(op) ->
console.log td!, "hi #{i}"
i--
<- wait-for \something
if i is 0
return op! # break
lo(op)
<- sleep 1500ms
<- :lo(op) ->
console.log td!, "hello #{i}"
i++
if i is 3
return op! # break
<- sleep 1000ms
lo(op)
<- sleep 0
console.log td!, "heyy"
do
a = 8
<- :lo(op) ->
console.log td!, "this runs in parallel!", a
a--
go \something
if a is 0
return op! # break
<- sleep 500ms
lo(op)
Output:
0ms : start
2ms : hi 3
3ms : this runs in parallel! 8
3ms : hi 2
505ms : this runs in parallel! 7
505ms : hi 1
1007ms : this runs in parallel! 6
1508ms : this runs in parallel! 5
2009ms : this runs in parallel! 4
2509ms : hello 0
2509ms : this runs in parallel! 3
3010ms : this runs in parallel! 2
3509ms : hello 1
3510ms : this runs in parallel! 1
4511ms : hello 2
4511ms : heyy
In javascript, there is no way, to make the code wait. I've had this problem and the way I did it was do a synchronous SJAX call to the server, and the server actually executes sleep or does some activity before returning and the whole time, the js waits.
Eg of Sync AJAX: http://www.hunlock.com/blogs/Snippets:_Synchronous_AJAX
I tried the callback way and could not get this to work, what you have to understand is that values are still atomic even though execution is not. For example:
alert('1'); <--- these two functions will be executed at the same time
alert('2'); <--- these two functions will be executed at the same time
but doing like this will force us to know the order of execution:
loop=2;
total=0;
for(i=0;i<loop;i++) {
total+=1;
if(total == loop)
alert('2');
else
alert('1');
}
Another way to look at this is to daisy chain from one function to another.
Have an array of functions that is global to all your called functions, say:
arrf: [ f_final
,f
,another_f
,f_again ],
Then setup an array of integers to the particular 'f''s you want to run, e.g
var runorder = [1,3,2,0];
Then call an initial function with 'runorder' as a parameter, e.g.
f_start(runorder);
Then at the end of each function, just pop the index to the next 'f' to execute off the runorder array and execute it, still passing 'runorder' as a parameter but with the array reduced by one.
var nextf = runorder.shift();
arrf[nextf].call(runorder);
Obviously this terminates in a function, say at index 0, that does not chain onto another function.
This is completely deterministic, avoiding 'timers'.
Put your code in a string, iterate, eval, setTimeout and recursion to continue with the remaining lines. No doubt I'll refine this or just throw it out if it doesn't hit the mark. My intention is to use it to simulate really, really basic user testing.
The recursion and setTimeout make it sequential.
Thoughts?
var line_pos = 0;
var string =`
console.log('123');
console.log('line pos is '+ line_pos);
SLEEP
console.log('waited');
console.log('line pos is '+ line_pos);
SLEEP
SLEEP
console.log('Did i finish?');
`;
var lines = string.split("\n");
var r = function(line_pos){
for (i = p; i < lines.length; i++) {
if(lines[i] == 'SLEEP'){
setTimeout(function(){r(line_pos+1)},1500);
return;
}
eval (lines[line_pos]);
}
console.log('COMPLETED READING LINES');
return;
}
console.log('STARTED READING LINES');
r.call(this,line_pos);
OUTPUT
STARTED READING LINES
123
124
1 p is 0
undefined
waited
p is 5
125
Did i finish?
COMPLETED READING LINES
I want to run some code on all my treeView nodes depending on a value returned from the database and repeat this until a certain value is returned.
I was thinking that:
Give all my tree nodes the same css class so I can access them from JQuery
have a timer in my JQuery function that used ajax to go to the database, when a certain value is returned then stop the timer
Two questions here. How can I make my function run for each of the nodes and how do I do a timer in JavaScript, so:
$(function(){
$('cssClassOfAllMyNodes').WhatFunctionToCallHere?((){
//How do I do Timer functionality in JavaScript?
ForEachTimeInterval
{
//use Ajax to go to database and retrieve a value
AjaxCallBackFunction(result)
{
if (result = 1)
//How to stop the timer here?
}
}
});
});
Hope i'm clear. Thanks a lot
thanks a lot for the answer. And i would like you to comment on the design.
Bascially what i'm trying to acheive is a Windows Wokflow type functionality where each node in my tree updates its image depending on its status, where its status is got from querying the database with a key unique to the tree node. I'm open to ideas on other ways to implement this if you have any. thanks again
Without commenting on your design you can refer to these
$.each()
setTimeout() or setInterval()
You can do:
$(function(){
$('cssClassOfAllMyNodes').each(function (){
// Do something with "this" - "this" refers to current node.
});
});
Te proper way to handle timers in JS is to have a reference to each timeout or interval and then clearing them out.
The difference between them is:
The timeout will only run once, unless stopped before;
The interval will run indefinitely, until stopped.
So you can do something like:
var delay = 2000; // miliseconds
var timer = setTimeout("functionToBeCalled", delay);
clearTimeout(timer); // whenever you need.
Please note you can pass a string to setTimeout (same with setInterval) with the name of the function to be called. Or you could pass a reference to the function itself:
var callback = function () { alert(1); };
var timer = setTimeout(callback, delay);
Be sure not to set an Interval for AJAX requests, because you response might be delayed and successive calls to the server could eventually overlap.
Instead, you should call setTimeout and when the answer arrives then call setTimeout again.