This javascript setTimeout interacts with ajax requests in a really weird way - javascript

I'm writing this script so that it displays the status of an import script. It's supposed to call a function, that runs a http request, every X seconds.
function progres_import() {
//if(import_status != 'finalizat') {
alert("progres_import");
setTimeout(function() { return update_progres_import(); }, 2000);
setTimeout(function() { return update_progres_import(); }, 4000);
setTimeout(function() { return update_progres_import(); }, 6000);
setTimeout(function() { return update_progres_import(); }, 8000);
//setTimeout(function() { progres_import(); }, 400);
//}
//else {
//}
}
this is what i used to test the functionality. I put the comments in too just to show what I intend to ultimately do with it. I tried all the possible setTimeout calls, with quotes, without quotes, with and without the anonymous function.
var xmlhttp_import_progres;
function update_progres_import() {
xmlhttp_import_progres=GetXMLHttpObject();
if (xmlhttp_import_progres==null) {
alert ("Browser does not support HTTP Request (xmlhttp_import_progres)");
return;
}
var url="crm/ferestre/import_progres.php";
url=url+"?sid="+Math.random();
xmlhttp_import_progres.onreadystatechange=function() {
if (xmlhttp_import_progres.readyState == 4) {
progres_resp = xmlhttp_import_progres.responseText;
progres = progres_resp.split('_');
import_nrc = progres[0];
import_nrt = progres[1];
import_status = progres[2];
mesaj = 'Progres import: ' + import_nrc + ' / ' + import_nrt;
//document.getElementById("corp_import_mesaj").innerHTML = mesaj;
alert(progres_resp);
}
};
xmlhttp_import_progres.open("POST",url,true);
xmlhttp_import_progres.send(null);
}
this is the business end of the progres_import function.
what happens is i get the alert("progress_import") in the first function right as the import process starts, but the alert(progres_resp) in the second one starts popping up only after the import process is over (it still maintains the 2 second interval so in that sense the setTimeouts worked).
the php script in the ajax request just takes some session variables that the import script sets and prints them for the javascript to use (x imports of y total, z failed, stuff like this)
Any idea why it behaves like this?

xmlhttp_import_progres.readyState == 4) is only true at the end of the request. Hence, your alert dialogs pop up after finishing the request.
Furthermore, you can't expect your function to show alerts after a 2 second interval, because the server may or may not respond as fast.
A final note: If you want to have a periodical update function, use setInterval(function(){...}, 2000).
EDIT
Also, add var in this way: var xmlhttp_import_progres = GetXMLHttpObject();. Currently, you're globally defining the HTTP object, causing only one instance of the HTTP object to be accessible.

Here, can you try to edit just a little:
Please consider the above answer, but this code will make clear for you:
function progres_import() {
//if(import_status != 'finalizat') {
alert("progres_import");
setTimeout(function() { return update_progres_import(0); }, 2000);
setTimeout(function() { return update_progres_import(1); }, 4000);
setTimeout(function() { return update_progres_import(2); }, 6000);
setTimeout(function() { return update_progres_import(3); }, 8000);
//setTimeout(function() { progres_import(); }, 400);
//}
//else {
//}
}
AND
var xmlhttp_import_progres = [];
function update_progres_import(i) {
xmlhttp_import_progres[i]= GetXMLHttpObject();
if (xmlhttp_import_progres[i]==null) {
alert ("Browser does not support HTTP Request (xmlhttp_import_progres)");
return;
}
var url="crm/ferestre/import_progres.php";
url=url+"?sid="+Math.random();
xmlhttp_import_progres[i].onreadystatechange=function() {
if (xmlhttp_import_progres[i].readyState == 4) {
progres_resp = xmlhttp_import_progres[i].responseText;
progres = progres_resp.split('_');
import_nrc = progres[0];
import_nrt = progres[1];
import_status = progres[2];
mesaj = 'Progres import: ' + import_nrc + ' / ' + import_nrt;
//document.getElementById("corp_import_mesaj").innerHTML = mesaj;
alert(progres_resp);
}
};
xmlhttp_import_progres[i].open("POST",url,true);
xmlhttp_import_progres[i].send(null);
}

Related

Several setTimeout() functions [duplicate]

I'm trying to send emails with a 10 seconds delay between. I wrote this code:
$(document).ready(function() {
for (i = 0; i < 20; i++) {
setTimeout("SendEmail(" + i + ")", 5000);
}
});
function SendEmail(id) {
$.get("mimesender.php?id=" + id, function(data) {
var toAppend = "<span> " + data + "</span>"
$("#sentTo").append(toAppend);
});
}
server side code(php) gets the id and selects the email with the specified id from the database
$query="select email from clienti where id =".$id;
then sends the email, and sends back the current email
echo email;
However, something is wrong here. It seems like the the js function waits for 5 seconds, and then displays all the 20 email addresses at once.
Can you tell me what I'm doing wrong ? any "sleep "workaround will be greatly appreciated :)
Use interval instead of loop.
Working demo: http://jsfiddle.net/xfVa9/2/
$(document).ready(function() {
var tmr;
var i=0;
tmr=setInterval(function(){
if(i<20){
SendEmail(i);
alert("Sent "+i)
i++;
}else{
clearInterval(tmr);
}
},5000)
});
You should create a function which calls itself after 5 seconds
var i=0;
function sendEmailNow() {
SendEmail(i);
++i;
if(i<20) {
setTimeout(sendEmailNow, 5000);
}
}
What happens is that you call setTimeout 20 times, just after one another, with a timeout of 5 seconds. So naturally, all emails gets sent at once. You could change the loop to look like this:
for (i=0;i<20;i++) {
setTimeout("SendEmail("+ i + ")",(i+1)*5000);
}
There's alot of other options though, and they'd depend on just what suits your specific problem best.
First, pass a function to setTimeout.
Secondly, you'd be better off if you set the timeout for the next one in the queue after the current one is finished.
In the for loop:
sendEmail(0); // start sending first
and in the callback:
, function(data) {
if(id < 19) { // if next should be sent
setTimeout(function() {
SendEmail(id + 1);
}, 5000);
}
var toAppend = "<span> " + data + "</span>"
$("#sentTo").append(toAppend);
}
Your loop is setting up 20 timers to wait 5 seconds, then letting them all go at once.
Try something like this:
var email_count = 20;
var sendMails = function(){
SendEmail(email_count--);
if(email_count > 0){
setTimeout(sendMails, 5000);
}
}
setTimeout(sendMails, 5000)
Avoid jQuery. Learn JavaScript.
var i = 0; (otherwise leak into outer scope or runtime error)
Extra closure:
window.setTimeout(
(function (j) {
return function () {
sendEmail(j);
};
}(i)),
i * 10000);
sendEmail (code style: not a constructor)
You really want to escape $id in the server-side code to prevent SQL injection.

JavaScript URL looper

I am trying create a page that will run some given URLs, in time this will be a database populated list of URLs loaded into an array. I have it working using some code I have altered, however I don't want it to keep looping, I just want it to stop once the list is complete.
So basically loads first URL, waits .5 of a second after the pages loads, then moves onto the next until the list of URLS is finished.
However, my code keeps looping. How do I prevent this?
var urls1 =
[
'http://localhost:8500/SupportTools/t1.cfm',
'http://localhost:8500/SupportTools/t2.cfm',
'http://localhost:8500/SupportTools/t3.cfm',
'http://localhost:8500/SupportTools/t4.cfm',
'http://localhost:8500/SupportTools/t5.cfm'
];
function showUrl1(idx)
{
var f1 = document.getElementById("f1");
f1.onload = function()
{
var next = ++idx % urls1.length;
setTimeout(function()
{
showUrl1(next);
}, 500);
}
f1.src = urls1[idx];
}
function start()
{
showUrl1(0);
}
<iframe class="show1" id="f1" src="about:blank"></iframe>
The reason it's looping is because you're using the remainder operator %. Just check the to see if next is greater than or equal to the length and then don't call setTimeout(). I've changed the snippet so that you can see it output in the console but you should get the idea.
var urls1 = [
'http://localhost:8500/SupportTools/t1.cfm',
'http://localhost:8500/SupportTools/t2.cfm',
'http://localhost:8500/SupportTools/t3.cfm',
'http://localhost:8500/SupportTools/t4.cfm',
'http://localhost:8500/SupportTools/t5.cfm'
];
function showUrl1(idx) {
if (idx >= urls1.length) {
return;
}
console.log(urls1[idx]);
var next = ++idx;
setTimeout(function() {
showUrl1(next);
}, 500);
}
function start() {
showUrl1(0);
}
start();
Dont showUrl when the list ends
function showUrl1(idx)
{
var f1 = document.getElementById("f1");
f1.onload = function()
{
var next = idx === urls1.length? null: ++idx ;
setTimeout(function()
{ if(next != null){
showUrl1(next);
}
}, 500);
}
f1.src = urls1[idx];
}

Execute javascript (PhantomJS) after load full webpage [duplicate]

I'm using PhantomJS v1.4.1 to load some web pages. I don't have access to their server-side, I just getting links pointing to them. I'm using obsolete version of Phantom because I need to support Adobe Flash on that web pages.
The problem is many web-sites are loading their minor content async and that's why Phantom's onLoadFinished callback (analogue for onLoad in HTML) fired too early when not everything still has loaded. Can anyone suggest how can I wait for full load of a webpage to make, for example, a screenshot with all dynamic content like ads?
Another approach is to just ask PhantomJS to wait for a bit after the page has loaded before doing the render, as per the regular rasterize.js example, but with a longer timeout to allow the JavaScript to finish loading additional resources:
page.open(address, function (status) {
if (status !== 'success') {
console.log('Unable to load the address!');
phantom.exit();
} else {
window.setTimeout(function () {
page.render(output);
phantom.exit();
}, 1000); // Change timeout as required to allow sufficient time
}
});
I would rather periodically check for document.readyState status (https://developer.mozilla.org/en-US/docs/Web/API/document.readyState). Although this approach is a bit clunky, you can be sure that inside onPageReady function you are using fully loaded document.
var page = require("webpage").create(),
url = "http://example.com/index.html";
function onPageReady() {
var htmlContent = page.evaluate(function () {
return document.documentElement.outerHTML;
});
console.log(htmlContent);
phantom.exit();
}
page.open(url, function (status) {
function checkReadyState() {
setTimeout(function () {
var readyState = page.evaluate(function () {
return document.readyState;
});
if ("complete" === readyState) {
onPageReady();
} else {
checkReadyState();
}
});
}
checkReadyState();
});
Additional explanation:
Using nested setTimeout instead of setInterval prevents checkReadyState from "overlapping" and race conditions when its execution is prolonged for some random reasons. setTimeout has a default delay of 4ms (https://stackoverflow.com/a/3580085/1011156) so active polling will not drastically affect program performance.
document.readyState === "complete" means that document is completely loaded with all resources (https://html.spec.whatwg.org/multipage/dom.html#current-document-readiness).
EDIT 2022:
I created this response 8 years ago and I did not use PhantomJS since then. It is very probable it won't work now in some cases. Also now I think it is not possible to create a one-size-fits-all solution to be absolutely sure the page is loaded. This is because some pages may load additional resources after document is ready. For example, there might be some JS code on the website that waits for the document to be ready an then loads some additional assets (after document state changes to ready) - in this case the onPageReady will trigger and after that the page will start loading some more resources again.
I still think the above snipped is a good starting point and may work in most cases, but may also necessary to create a specific solutions to handle specific websites.
You could try a combination of the waitfor and rasterize examples:
/**
* See https://github.com/ariya/phantomjs/blob/master/examples/waitfor.js
*
* Wait until the test condition is true or a timeout occurs. Useful for waiting
* on a server response or for a ui change (fadeIn, etc.) to occur.
*
* #param testFx javascript condition that evaluates to a boolean,
* it can be passed in as a string (e.g.: "1 == 1" or "$('#bar').is(':visible')" or
* as a callback function.
* #param onReady what to do when testFx condition is fulfilled,
* it can be passed in as a string (e.g.: "1 == 1" or "$('#bar').is(':visible')" or
* as a callback function.
* #param timeOutMillis the max amount of time to wait. If not specified, 3 sec is used.
*/
function waitFor(testFx, onReady, timeOutMillis) {
var maxtimeOutMillis = timeOutMillis ? timeOutMillis : 3000, //< Default Max Timout is 3s
start = new Date().getTime(),
condition = (typeof(testFx) === "string" ? eval(testFx) : testFx()), //< defensive code
interval = setInterval(function() {
if ( (new Date().getTime() - start < maxtimeOutMillis) && !condition ) {
// If not time-out yet and condition not yet fulfilled
condition = (typeof(testFx) === "string" ? eval(testFx) : testFx()); //< defensive code
} else {
if(!condition) {
// If condition still not fulfilled (timeout but condition is 'false')
console.log("'waitFor()' timeout");
phantom.exit(1);
} else {
// Condition fulfilled (timeout and/or condition is 'true')
console.log("'waitFor()' finished in " + (new Date().getTime() - start) + "ms.");
typeof(onReady) === "string" ? eval(onReady) : onReady(); //< Do what it's supposed to do once the condition is fulfilled
clearInterval(interval); //< Stop this interval
}
}
}, 250); //< repeat check every 250ms
};
var page = require('webpage').create(), system = require('system'), address, output, size;
if (system.args.length < 3 || system.args.length > 5) {
console.log('Usage: rasterize.js URL filename [paperwidth*paperheight|paperformat] [zoom]');
console.log(' paper (pdf output) examples: "5in*7.5in", "10cm*20cm", "A4", "Letter"');
phantom.exit(1);
} else {
address = system.args[1];
output = system.args[2];
if (system.args.length > 3 && system.args[2].substr(-4) === ".pdf") {
size = system.args[3].split('*');
page.paperSize = size.length === 2 ? {
width : size[0],
height : size[1],
margin : '0px'
} : {
format : system.args[3],
orientation : 'portrait',
margin : {
left : "5mm",
top : "8mm",
right : "5mm",
bottom : "9mm"
}
};
}
if (system.args.length > 4) {
page.zoomFactor = system.args[4];
}
var resources = [];
page.onResourceRequested = function(request) {
resources[request.id] = request.stage;
};
page.onResourceReceived = function(response) {
resources[response.id] = response.stage;
};
page.open(address, function(status) {
if (status !== 'success') {
console.log('Unable to load the address!');
phantom.exit();
} else {
waitFor(function() {
// Check in the page if a specific element is now visible
for ( var i = 1; i < resources.length; ++i) {
if (resources[i] != 'end') {
return false;
}
}
return true;
}, function() {
page.render(output);
phantom.exit();
}, 10000);
}
});
}
Here is a solution that waits for all resource requests to complete. Once complete it will log the page content to the console and generate a screenshot of the rendered page.
Although this solution can serve as a good starting point, I have observed it fail so it's definitely not a complete solution!
I didn't have much luck using document.readyState.
I was influenced by the waitfor.js example found on the phantomjs examples page.
var system = require('system');
var webPage = require('webpage');
var page = webPage.create();
var url = system.args[1];
page.viewportSize = {
width: 1280,
height: 720
};
var requestsArray = [];
page.onResourceRequested = function(requestData, networkRequest) {
requestsArray.push(requestData.id);
};
page.onResourceReceived = function(response) {
var index = requestsArray.indexOf(response.id);
if (index > -1 && response.stage === 'end') {
requestsArray.splice(index, 1);
}
};
page.open(url, function(status) {
var interval = setInterval(function () {
if (requestsArray.length === 0) {
clearInterval(interval);
var content = page.content;
console.log(content);
page.render('yourLoadedPage.png');
phantom.exit();
}
}, 500);
});
Maybe you can use the onResourceRequested and onResourceReceived callbacks to detect asynchronous loading. Here's an example of using those callbacks from their documentation:
var page = require('webpage').create();
page.onResourceRequested = function (request) {
console.log('Request ' + JSON.stringify(request, undefined, 4));
};
page.onResourceReceived = function (response) {
console.log('Receive ' + JSON.stringify(response, undefined, 4));
};
page.open(url);
Also, you can look at examples/netsniff.js for a working example.
In my program, I use some logic to judge if it was onload: watching it's network request, if there was no new request on past 200ms, I treat it onload.
Use this, after onLoadFinish().
function onLoadComplete(page, callback){
var waiting = []; // request id
var interval = 200; //ms time waiting new request
var timer = setTimeout( timeout, interval);
var max_retry = 3; //
var counter_retry = 0;
function timeout(){
if(waiting.length && counter_retry < max_retry){
timer = setTimeout( timeout, interval);
counter_retry++;
return;
}else{
try{
callback(null, page);
}catch(e){}
}
}
//for debug, log time cost
var tlogger = {};
bindEvent(page, 'request', function(req){
waiting.push(req.id);
});
bindEvent(page, 'receive', function (res) {
var cT = res.contentType;
if(!cT){
console.log('[contentType] ', cT, ' [url] ', res.url);
}
if(!cT) return remove(res.id);
if(cT.indexOf('application') * cT.indexOf('text') != 0) return remove(res.id);
if (res.stage === 'start') {
console.log('!!received start: ', res.id);
//console.log( JSON.stringify(res) );
tlogger[res.id] = new Date();
}else if (res.stage === 'end') {
console.log('!!received end: ', res.id, (new Date() - tlogger[res.id]) );
//console.log( JSON.stringify(res) );
remove(res.id);
clearTimeout(timer);
timer = setTimeout(timeout, interval);
}
});
bindEvent(page, 'error', function(err){
remove(err.id);
if(waiting.length === 0){
counter_retry = 0;
}
});
function remove(id){
var i = waiting.indexOf( id );
if(i < 0){
return;
}else{
waiting.splice(i,1);
}
}
function bindEvent(page, evt, cb){
switch(evt){
case 'request':
page.onResourceRequested = cb;
break;
case 'receive':
page.onResourceReceived = cb;
break;
case 'error':
page.onResourceError = cb;
break;
case 'timeout':
page.onResourceTimeout = cb;
break;
}
}
}
I found this approach useful in some cases:
page.onConsoleMessage(function(msg) {
// do something e.g. page.render
});
Than if you own the page put some script inside:
<script>
window.onload = function(){
console.log('page loaded');
}
</script>
I found this solution useful in a NodeJS app.
I use it just in desperate cases because it launches a timeout in order to wait for the full page load.
The second argument is the callback function which is going to be called once the response is ready.
phantom = require('phantom');
var fullLoad = function(anUrl, callbackDone) {
phantom.create(function (ph) {
ph.createPage(function (page) {
page.open(anUrl, function (status) {
if (status !== 'success') {
console.error("pahtom: error opening " + anUrl, status);
ph.exit();
} else {
// timeOut
global.setTimeout(function () {
page.evaluate(function () {
return document.documentElement.innerHTML;
}, function (result) {
ph.exit(); // EXTREMLY IMPORTANT
callbackDone(result); // callback
});
}, 5000);
}
});
});
});
}
var callback = function(htmlBody) {
// do smth with the htmlBody
}
fullLoad('your/url/', callback);
This is an implementation of Supr's answer. Also it uses setTimeout instead of setInterval as Mateusz Charytoniuk suggested.
Phantomjs will exit in 1000ms when there isn't any request or response.
// load the module
var webpage = require('webpage');
// get timestamp
function getTimestamp(){
// or use Date.now()
return new Date().getTime();
}
var lastTimestamp = getTimestamp();
var page = webpage.create();
page.onResourceRequested = function(request) {
// update the timestamp when there is a request
lastTimestamp = getTimestamp();
};
page.onResourceReceived = function(response) {
// update the timestamp when there is a response
lastTimestamp = getTimestamp();
};
page.open(html, function(status) {
if (status !== 'success') {
// exit if it fails to load the page
phantom.exit(1);
}
else{
// do something here
}
});
function checkReadyState() {
setTimeout(function () {
var curentTimestamp = getTimestamp();
if(curentTimestamp-lastTimestamp>1000){
// exit if there isn't request or response in 1000ms
phantom.exit();
}
else{
checkReadyState();
}
}, 100);
}
checkReadyState();
This the code I use:
var system = require('system');
var page = require('webpage').create();
page.open('http://....', function(){
console.log(page.content);
var k = 0;
var loop = setInterval(function(){
var qrcode = page.evaluate(function(s) {
return document.querySelector(s).src;
}, '.qrcode img');
k++;
if (qrcode){
console.log('dataURI:', qrcode);
clearInterval(loop);
phantom.exit();
}
if (k === 50) phantom.exit(); // 10 sec timeout
}, 200);
});
Basically given the fact you're supposed to know that the page is full downloaded when a given element appears on the DOM. So the script is going to wait until this happens.
I use a personnal blend of the phantomjs waitfor.js example.
This is my main.js file:
'use strict';
var wasSuccessful = phantom.injectJs('./lib/waitFor.js');
var page = require('webpage').create();
page.open('http://foo.com', function(status) {
if (status === 'success') {
page.includeJs('https://cdnjs.cloudflare.com/ajax/libs/jquery/3.1.1/jquery.min.js', function() {
waitFor(function() {
return page.evaluate(function() {
if ('complete' === document.readyState) {
return true;
}
return false;
});
}, function() {
var fooText = page.evaluate(function() {
return $('#foo').text();
});
phantom.exit();
});
});
} else {
console.log('error');
phantom.exit(1);
}
});
And the lib/waitFor.js file (which is just a copy and paste of the waifFor() function from the phantomjs waitfor.js example):
function waitFor(testFx, onReady, timeOutMillis) {
var maxtimeOutMillis = timeOutMillis ? timeOutMillis : 3000, //< Default Max Timout is 3s
start = new Date().getTime(),
condition = false,
interval = setInterval(function() {
if ( (new Date().getTime() - start < maxtimeOutMillis) && !condition ) {
// If not time-out yet and condition not yet fulfilled
condition = (typeof(testFx) === "string" ? eval(testFx) : testFx()); //< defensive code
} else {
if(!condition) {
// If condition still not fulfilled (timeout but condition is 'false')
console.log("'waitFor()' timeout");
phantom.exit(1);
} else {
// Condition fulfilled (timeout and/or condition is 'true')
// console.log("'waitFor()' finished in " + (new Date().getTime() - start) + "ms.");
typeof(onReady) === "string" ? eval(onReady) : onReady(); //< Do what it's supposed to do once the condi>
clearInterval(interval); //< Stop this interval
}
}
}, 250); //< repeat check every 250ms
}
This method is not asynchronous but at least am I assured that all the resources were loaded before I try using them.
This is an old question, but since I was looking for full page load but for Spookyjs (that uses casperjs and phantomjs) and didn't find my solution, I made my own script for that, with the same approach as the user deemstone .
What this approach does is, for a given quantity of time, if the page did not receive or started any request it will end the execution.
On casper.js file (if you installed it globally, the path would be something like /usr/local/lib/node_modules/casperjs/modules/casper.js) add the following lines:
At the top of the file with all the global vars:
var waitResponseInterval = 500
var reqResInterval = null
var reqResFinished = false
var resetTimeout = function() {}
Then inside function "createPage(casper)" just after "var page = require('webpage').create();" add the following code:
resetTimeout = function() {
if(reqResInterval)
clearTimeout(reqResInterval)
reqResInterval = setTimeout(function(){
reqResFinished = true
page.onLoadFinished("success")
},waitResponseInterval)
}
resetTimeout()
Then inside "page.onResourceReceived = function onResourceReceived(resource) {" on the first line add:
resetTimeout()
Do the same for "page.onResourceRequested = function onResourceRequested(requestData, request) {"
Finally, on "page.onLoadFinished = function onLoadFinished(status) {" on the first line add:
if(!reqResFinished)
{
return
}
reqResFinished = false
And that's it, hope this one helps someone in trouble like I was. This solution is for casperjs but works directly for Spooky.
Good luck !
this is my solution its worked for me .
page.onConsoleMessage = function(msg, lineNum, sourceId) {
if(msg=='hey lets take screenshot')
{
window.setInterval(function(){
try
{
var sta= page.evaluateJavaScript("function(){ return jQuery.active;}");
if(sta == 0)
{
window.setTimeout(function(){
page.render('test.png');
clearInterval();
phantom.exit();
},1000);
}
}
catch(error)
{
console.log(error);
phantom.exit(1);
}
},1000);
}
};
page.open(address, function (status) {
if (status !== "success") {
console.log('Unable to load url');
phantom.exit();
} else {
page.setContent(page.content.replace('</body>','<script>window.onload = function(){console.log(\'hey lets take screenshot\');}</script></body>'), address);
}
});
Do Mouse move while page is loading should work.
page.sendEvent('click',200, 660);
do { phantom.page.sendEvent('mousemove'); } while (page.loading);
UPDATE
When submitting the form, nothing was returned, so the program stopped. The program did not wait for the page to load as it took a few seconds for the redirect to begin.
telling it to move the mouse until the URL changes to the home page gave the browser as much time as it needed to change. then telling it to wait for the page to finish loading allowed the page to full load before the content was grabbed.
page.evaluate(function () {
document.getElementsByClassName('btn btn-primary btn-block')[0].click();
});
do { phantom.page.sendEvent('mousemove'); } while (page.evaluate(function()
{
return document.location != "https://www.bestwaywholesale.co.uk/";
}));
do { phantom.page.sendEvent('mousemove'); } while (page.loading);

Synchronous Call in JavaScript is not working

I am creating a hotel page which contains all the details of hotels including its reviews. For the reviews of the hotel, i want to do pagination.
In my code, init() function contain all the code for performing initial calculations like calculating no of pages etc.
As you can see init() is calling a function getReviewList which will return list of reviews. In getReviewList(), making a synchronous call to receive lists of reviews(i.e. reviewList.json).
The goal is once i receive the list of reviews, i can perform all the calculations. Even i am using setTimeout function so that this action get delayed by some time and in this mean time, the response get stored in the global variable "reviewList".
But the problem is that the calculation part gets executed before the response is stored in the global variable "reviewList". Please guide me where i am going wrong.
Javascript (fiddle : http://jsfiddle.net/n28SS/) fiddle contains incomplete code
getReviewList: function () {
var myReq, reqJSON,
oThis = this;
if(window.XMLHttpRequest) {
myReq = new XMLHttpRequest();
} else {
myReq = new ActiveXObject("Microsoft.XMLHTTP");
}
myReq.open("GET", "reviewList.json", true);
myReq.send();
myReq.onreadystatechange = function() {
if (myReq.readyState == 4) {
if (myReq.status == 200) {
oThis.reviewList = JSON.parse(myReq.responseText);
}
}
}
},
init: function() {
var oThis = this,
records;
records = oThis.getReviewList();
setTimeout(function () {
records = oThis.reviewList.length;
}, 1000);
oThis.pages = Math.ceil(records / oThis.itemsPerPage);
oThis.inited = true;
oThis.attachEventListener();
},
This doesn't work because
setTimeout(function () {
records = oThis.reviewList.length;
}, 1000);
returns immediately and performs the records=... operation in the background after 1000 ms.
The following line executes immediately and hence doesn't have access to records variable
oThis.pages=Math.ceil(records/...)
which will become available 1sec later. You can either move all the code into the set timeout block, or better yet make everything asynchronous:
getReviewList: function (callback) {//pass the function that needs to act on list
var myReq, reqJSON,
oThis = this;
if(window.XMLHttpRequest) {
myReq = new XMLHttpRequest();
} else {
myReq = new ActiveXObject("Microsoft.XMLHTTP");
}
myReq.open("GET", "reviewList.json", true);
myReq.send();
myReq.onreadystatechange = function() {
if (myReq.readyState == 4) {
if (myReq.status == 200) {
oThis.reviewList = JSON.parse(myReq.responseText);
callback(oThis.reviewList);
}
}
}
},
init: function() {
var oThis = this,
records;
records = oThis.getReviewList(function (reviewList) { //Pass the function as a parameter
records = reviewList.length;
oThis.pages = Math.ceil(records / oThis.itemsPerPage);
oThis.inited = true;
oThis.attachEventListener();
});
},
I haven't tested the code but it should demonstrate the basic concept.

Need to delay javascript execution, but setTimeout is proving problematic

Thank you for taking the time to help me.
I am writing a game where an animated train icon moves along a given path to a destination, pausing at waypoints along the way. This is intended to give the impression of animation.
The game is coded in Facebook Javascript. I need to find a way to make the train icon pause for 1 second before moving on to the next waypoint. I hoped to find a function that would allow me to pause script execution for one second, but nothing like that seems to exist in JS. So I tried setTimeout, but my primary problem with this is twofold:
I need to pass an array into the callback function as an argument, and I can't figure out how to make setTimeout do this.
I finally succeeded in using setTimeout to execute my train animation code for 5 waypoints (I overcame the issue in 1 by using global variables). Unfortunately, it appears that all five calls to setTimeout got queued almost simultaneously, which resulted in waiting one second for the first setTimeout to fire, thenn they all fired at once ruining the illusion of train animation.
I've been battling this problem for six hours straight. It would be wonderful if someone could help me find a solution. Thanks!
Here's the code:
function myEventMoveTrainManual(evt, performErrorCheck) {
if(mutexMoveTrainManual == 'CONTINUE') {
var ajax = new Ajax();
var param = {};
if(evt) {
var cityId = evt.target.getParentNode().getId();
var param = { "city_id": cityId };
}
ajax.responseType = Ajax.JSON;
ajax.ondone = function(data) {
var actionPrompt = document.getElementById('action-prompt');
actionPrompt.setInnerXHTML('<span><div id="action-text">'+
'Train en route to final destination...</div></span>');
for(var i = 0; i < data.length; i++) {
statusFinalDest = data[i]['status_final_dest'];
//pause(1000);
gData = data[i];
setTimeout(function(){drawTrackTimeout()},1000);
if(data[i]['code'] == 'UNLOAD_CARGO' && statusFinalDest == 'ARRIVED') {
unloadCargo();
} else if (data[i]['code'] == 'MOVE_TRAIN_AUTO' || data[i]['code'] == 'TURN_END') {
//moveTrainAuto();
} else {
// handle error
}
mutexMoveTrainManual = 'CONTINUE';
}
}
ajax.post(baseURL + '/turn/move-train-final-dest', param);
}
}
function drawTrackTimeout() {
var trains = [];
trains[0] = gData['train'];
removeTrain(trains);
drawTrack(gData['y1'], gData['x1'], gData['y2'], gData['x2'], '#FF0', trains);
gData = null;
}
Typically this would be done by creating an object (say called myTrain) that has all its own data and methods, then call a myTrain.run mehod that looks to see where the train is. If it's between two stations, it calls itself with setTimeout and say a 50ms delay. When it reaches a station, it calls itself in 1000ms, creating a 1 second pause at the station.
If you queue the setTimeouts all at once, you run the risk of them all being delayed by some other process, then all running at once.
Hey, bit of fun (careful of wrapping). Needed a bit of practice with good 'ole prototype inheritance:
<!-- All the style stuff should be in a rule -->
<div style="position: relative; border: 1px solid blue;">
<div id="redTrain"
style="width:10px;height:10px;background-color:red; position:absolute;top:0px;left:0px;"></div>
</div>
<script type="text/javascript">
// Train constructor
function Train(id) {
this.element = document.getElementById(id);
this.timerId;
}
// Methods
// Trivial getPos function
Train.prototype.getPos = function() {
return this.element.style.left;
}
// Trivial setPos function
Train.prototype.setPos = function(px) {
this.element.style.left = parseInt(px,10) + 'px';
}
// Move it px pixels to the right
Train.prototype.move = function(px) {
this.setPos(px + parseInt(this.getPos(),10));
}
// Recursive function using setTimeout for animation
// Probably should accept a parameter for lag, long lag
// should be a multiple of lag
Train.prototype.run = function() {
// If already running, stop it
// so can interrupt a pause with a start
this.stop();
// Move the train
this.move(5);
// Keep a reference to the train for setTimeout
var train = this;
// Default between each move is 50ms
var lag = 50;
// Pause for 1 second each 100px
if (!(parseInt(this.getPos(),10) % 100)) {
lag = 1000;
}
train.timerId = window.setTimeout( function(){train.run();}, lag);
}
// Start should do a lot more initialising
Train.prototype.start = function() {
this.run();
}
// Stops the train until started again
Train.prototype.stop = function() {
if (this.timerId) {
clearTimeout(this.timerId);
}
}
// Set back to zero
Train.prototype.reset = function() {
this.stop();
this.setPos(0);
}
// Initialise train here
var myTrain = new Train('redTrain');
</script>
<p> </p>
<button onclick="myTrain.start();">Start the train</button>
<button onclick="myTrain.stop();">Stop the train</button>
<button onclick="myTrain.reset();">Reset the train</button>
To pass arguments, this might help you:
setTimeout(function() {
(function(arg1, arg2) {
// you can use arg1 / arg2 here
})('something', 123);
}, 1000);
Or, if you use a defined function:
setTimeout(function() {
someFunction('something', 123);
}, 1000);
It basically starts a timeout; after one second the function is invoked with the specified arguments.
How about using OO principles to simplify the problem? Create an "object" Train which has the following methods:
//train obj
function Train(){
this.isOnWaypoint = function(){
return calculateIsWayPoint()
}
}
//main logic
var train = new Train()
var doneWaiting = false
var doneWaitingTimeout = undefined
var gameLoop = setInterval(1000,function(){
...
if(train.isOnWaypoint() && !doneWaiting){
if(doneWaitingTimeout == undefined){
setTimeOut(5000,function(){
doneWaiting = true
doneWaitingTimeout = undefined
})
}
}
...
})
Here's the solution I finally came up with:
function drawTrackTimeout() {
if(gData != null && gIndex < gData.length) {
var trains = [];
trains[0] = gData[gIndex]['train'];
removeTrain(trains);
drawTrack(gData[gIndex]['y1'], gData[gIndex]['x1'], gData[gIndex]['y2'], gData[gIndex]['x2'], '#FF0', trains);
statusFinalDest = gData[gIndex]['status_final_dest'];
if(statusFinalDest == 'ARRIVED') {
unloadCargo();
} else if (gData[gIndex]['code'] == 'MOVE_TRAIN_AUTO' || gData[gIndex]['code'] == 'TURN_END') {
//moveTrainAuto();
} else {
// handle error
}
gIndex++;
} else {
clearInterval(gIntid);
gIntid = null;
gData = null;
gIndex = 0;
}
}
function myEventMoveTrainManual(evt, performErrorCheck) {
//debugger;
if(mutexMoveTrainManual == 'CONTINUE') {
var ajax = new Ajax();
var param = {};
if(evt) {
var cityId = evt.target.getParentNode().getId();
var param = { "city_id": cityId };
}
ajax.responseType = Ajax.JSON;
ajax.ondone = function(data) {
var actionPrompt = document.getElementById('action-prompt');
actionPrompt.setInnerXHTML('<span><div id="action-text">'+
'Train en route to final destination...</div></span>');
gData = data;
gIndex = 0;
gIntid = setInterval(function(){drawTrackTimeout()},1000);
}
ajax.post(baseURL + '/turn/move-train-final-dest', param);
}
}

Categories

Resources