I have a webpage that I'm using to print 'pages' of data as PDF files via the firefox 'Print to File' printer on my laptop. The code I'm invoking is as follows:
document.body.controls.cmdPrint.click = function () // Create a function that will be called when this object is clicked upon
{if (parseInt(document.body.controls.page.innerHTML) !== 0) // If we are not on the Front Cover
{return false;} // Function complete: Abnormal Termination
document.body.controls.style.pointerEvents = 'none'; // Lock down the controls so they cannot be interfered with
do // Do...
{window.print(); // Print this page
// document.body.sleep(); // Removed as this does not work as expected (see below...)
} while (document.body.controls.cmdNext.click()) // ...while we are able to advance.
document.body.controls.style.pointerEvents = ''; // Release the controls lockout
this.blur(); // Blur the focus
return true;}; // Function complete: Normal Termination
When executing, the pages flip as expected (as the cmdNext.click() function returns a true when successful and a false when it's on the last page and trying to advance), but it runs too quickly. Namely every odd page is caught out, as the 'printer' is unavailable....the window.print() is being released before the printer is ready for the next page.
I tried slowing down execution by adding a reference to a secondary function within the loop (now commented out), but this just locks up the CPU and keeps the printer from processing in another thread....so it's not a valid solution. This function (which I wrote but did not provide the expected cushioning to allow odd pages to print) is as follows.
document.body.sleep = function (delay) // Create a new function
{delay = delay || 1; // Default to a delay of 1 second
var timestamp = new Date(); // Get current time
timestamp = new Date(timestamp.getTime() + (delay * 1000)); // Add in the delay (in seconds)
while (new Date() < timestamp) {} // While we are waiting for the delay, do nothing
return true;}; // Function complete: Normal Termination
Basically what I need is a way to hold loop execution long enough to let the 'Print to File' to go through before the next window.print() is called. Using the sleep function above also keeps the window.print() from running even though (when I was trying to use it as a fix) I had put the call to this function immediately after the window.print() command.
So I ask, can anyone here supply a fix for this project so I don't have to manually cycle through each page (which could get annoyingly temporally expensive with a page count above 10)?
As it is currently, when the second page tries to print, I get the popup window holding an error from firefox: "Printer Error - Some printing functionality is not currently available." Tracking this down lead to PERR_NOT_AVAILABLE....probably because the printer (Print to File) is busy printing the prior page....so I just need to wait for that to resolve before going to the next printing. An error handler to catch this PERR_NOT_AVAILABLE instead of letting it bounce to the user (me) as a popup window that has to be clicked out of would be nice, though a spammy way of getting the pages to print in sequence as quickly as the Print to File system can process them.
If window.print() actually returned an error in this condition, I could just rerun the command...
In my experimentation, I found this workaround...it's quite fairly considered a kludge, but in the absence of a more elegant solution, it works.
document.body.controls.cmdPrint.click = function () // Create a function that will be called when this object is clicked upon
{if (parseInt(document.body.controls.page.innerHTML) !== 0) // If we are not on the Front Cover
{return false;} // Function complete: Abnormal Termination
document.body.controls.style.pointerEvents = 'none'; // Lock down the controls so they cannot be interfered with
window.onafterprint = function () // Set up a handler for after a print operation
{setTimeout(function () // Run a delayed operation
{if (document.body.controls.cmdNext.click()) // Move to next page and if this is successful
{window.print(); // Continue printing
return false;} // Function complete: Still Printing
do // Do nothing...
{} while (document.body.controls.cmdPrevious.click()) // ...while we cycle back to the start
document.body.controls.style.pointerEvents = ''; // Release the controls lockout
this.blur(); // Blur the focus
window.onafterprint = function () {}; // Remove this handler
return true;}, 2000);}; // Function complete: Normal Termination
window.print(); // Begin printing the page
return true;}; // Function complete: Normal Termination
If a page is too complex to print to PDF in 2 seconds, that 2000 figure in the setTimeout() will have to be increased to account for it. For my test case (the current 7 page document), 2000 seems to be the sweet spot between not-working and spitting out pages as quickly as Firefox will permit on my system.
Related
I know that this might be a stupid question but it drives me crazy. I'm trying to change the innerHTML of a DOM element but it doesn't change until the end of the function's execution. For example:
function test(){
let testEl = document.getElementById('testEl')
for (let i = 0; i < 5; i++)
{
testEl.innerHTML = 'Count: ' + i;
alert(i);
}
}
Even if I have put an alert in the loop, the text of the element will not change until the end of the function's execution. How can the change be applied instantly (for example I mean during the loop)?
You can update the number every period of time using setInterval:
function test(){
let testEl = document.getElementById('testEl');
let i = 0;
const interval = setInterval(function(){
testEl.innerHTML = `Count: ${i++}`;
if(i === 5)
clearInterval(interval);
}, 1000);
}
test();
<p id="testEl"></p>
JavaScript runs in a single-threaded environment. This means that only one execution context can ever be running at any single point in time. Asynchronous code executes outside of the JavaScript runtime environment (in this case by the browser's native processing) and only when the JavaScript thread is idle can the results of an asynchronous request be executed (i.e. callbacks).
Below is an example that updates a DOM element approximately every second, creating a clock. However, if you click the button, it will ask the browser to render an alert, which is handled outside of the JavaScript runtime and is a blocking UI element, so the clock will stop. Once you clear the alert, you will see the time jump to be roughly current.
As you'll see, the asynchronous API call to window.setInterval() allows for the function to run repeatedly, every so often, and therefore not continuously. This replaces the need for a loop that runs in its entirety every time its accessed. Because of this, you can see updates to the webpage instead of the last value of your loop.
See the comments for more details:
const clock = document.querySelector("span");
// setInterval is not JavaScript. It's a call to a browser
// API asking the JS runtime to run the supplied function every
// 900 milliseconds, but that's just a request. After 900
// milliseconds, the browser will place the function on the
// JavaScript event queue and only when the JavaScript thread
// is idle will anything on the queue be executed. This is why
// the 900 milliseconds is not a guarantee - - it's just the
// minimum amount of time you'll have to wait for the function
// to run, but it could be longer if what's already running
// on the JavaScript thread takes longer than 900 milliseconds
// to complete.
window.setInterval(function(){
// Update the DOM
clock.textContent = new Date().toLocaleTimeString();
}, 900);
document.querySelector("button").addEventListener("click", function(){
// An alert is also not JavaScript, but another browser API that is executed
// by the browser, not JavaScript. However, it is a blocking (modal) UI element.
// The rest of the browser interface (including the web page) cannot update
// while the alert is present. As soon as the alert is cleared, the UI will update.
window.alert("I'm a UI blocking construct rendered by the browser, not JavaScript");
});
<div>Current time is: <span></span></div>
<button>Click for alert</button>
Another way to achieve it is by using async - Promise like this
async function test() {
let testEl = document.getElementById('testEl')
for (let i = 0; i < 5; ++i) {
testEl.innerHTML = 'Count: ' + i;
await new Promise((resolve, _) => {
setTimeout(function() {
resolve("");
}, 1000 /* your preferred delay here */);
});
}
}
When using waitForKeyElement.js I was wondering if there is a good solution to implementing a max time to wait? Sometimes I have elements that are supposed to be there, but sometimes on a poor ajax response they do not appear, or they may take a while to appear. In cases like that I simply want it to move on and execute a separate function.
Edit: For example, when trying to "buy" an item it typically always responds with a "success" box which is what the program is waiting for, however sometimes this box does not happen if the server has an error or if the item is no longer available to "buy" in which case a different error element may be inserted instead, however this is a different element and as such it continues to wait instead of continuing on to process the next item.
The question does not have an example use case, nor MCVE. There's a good chance that it is an XY Problem.
That said, there's no elegant way to set a max wait time (because there's never been neither a need, nor a demand for one before).
You can get the effect with code like:
const maxTime = 5; // seconds
var watchdogTimer = setTimeout (fallBackFunc, maxTime * 1000);
waitForKeyElements ("#foo", desiredFunc, true);
function desiredFunc (jNode) {
console.log ("Foo found!");
clearTimeout (watchdogTimer);
}
function fallBackFunc () {
console.log ("Oh, poo. No foo.");
}
Unloading the waitForKeyElements timer in such a scenario should not be necessary, but you can also do that by adding the following code at the end of fallBackFunc():
function fallBackFunc () {
console.log ("Oh, poo. No foo.");
/*-- Optional body double to take fire from waitForKeyElements that the
has `true` parameter set. This is almost never needed.
*/
var nonce = "IdeallySomeGloballyUniqueStringThatIs_a_ValidCssClass";
//-- Added node should have properties that match the WFKE selector
$("body").append (`<span id="foo" class="${nonce}" style="display:none;">blah</span>`);
setTimeout (function () {$(`.${nonce}`).remove(); }, 333); // time must be > 300
}
Regarding the question edit:
... (the page) responds with a "success" box which is what the program is waiting for, however sometimes this box does not happen... in which case a different error element may be inserted instead...
The usual practice for that is to set waitForKeyElements to listen for both the success node and the error node. (See jQuery selectors doc.)
The waitForKeyElementscallback would then take the appropriate action for the type of node passed to it.
No watchdog timer is needed (unless the page can stay active and yet not return either node type -- which is almost never the case).
For example:
waitForKeyElements ("#goodNode, #errorNode", completeTransaction, true);
function completeTransaction (jNode) {
if (jNode.is ("#goodNode") ) { // Match one of the WFKE selectores
console.log ("Transaction success!");
}
else {
console.log ("Transaction error.");
}
}
As a bonus, this approach: (A) doesn't have to wait for a max timer to run out and (B) is not subject to breaking if max timer is not set long enough for every circumstance.
I'm calling this a browser crash, but more like an infinite loop - I'm hardly a javascript expert. What happens is a game gets to it's end, and the browser (in this case firefox) just goes on and on and on - more like a freeze, no snap, error message, no nothing. You have to close down the browser completely. Now the really odd thing about this is this works perfectly fine - off line. This just happens online. And the other odd thing about this is that (in the code) the first time it resets the game - this also works perfect both online and off. Just the second time.
Like this: (this is part of it)
function resetGame(){
//reset game
Ok the above works online and off, player gets dumped out of game, starts over. Then however;
function moveToEndGame(){
console.log("TIMER END");
var twn = game.add.tween(bg).to({alpha:0},tween_speed,"Linear",true);
twn.onComplete.addOnce(function(){flagGameover = true;},this);
}
if(!flagGameover && !star.visible && idx_bullet < bullet_array.length)
initBullet();
else{
if(flagGameover){
console.log("GOTO GAMEOVER");
window.location = "../endgame.html";
}
}
}
}
OK, offline it goes directly to endgame.html online, it freezes up, crashes, infinitive loop, whatever you want to call it.
Anyone have any idea why?
OK, spent hours and hours on this (the programmer who did this part has since disappeared). Even went back 2 weeks ago to the code as it was, yup, that does the same thing - infinite loop.
So now I'd like to find a way to just 'abort the javascript' right at the end of the 21 second timer. I've run across a number of things from other posts such as:
// creates a new exception type:
function FatalError(){ Error.apply(this, arguments); this.name = "FatalError"; }
FatalError.prototype = Object.create(Error.prototype);
or
function javascript_abort()
But....
Even if you throw an exception, it will only kill the current event loop. Callbacks passed to setTimeout or DOM/XMLHttpRequest event handlers will still run when their time comes.
or something with this:
functions (setTimer and two setIntervals)
OR
function clearTimer() {
clearInterval(intervalTimer);
}
Anyway, here are the sections in the code pertaining to the 21 second timer:
var maxTime = 21; //in SECOND
var timerEvent = null;
This is where the timer gets added (after a 3 second animation)
console.log("FINISH ANIMATION and ADD TIMER");
and this
timerEvent = game.time.events.add(Phaser.Timer.SECOND * maxTime, moveToEndGame, this);
},this);
function moveToEndGame(){
console.log("TIMER END");
var twn = game.add.tween(bg).to({alpha:0},tween_speed,"Linear",true);
twn.onComplete.addOnce(function(){flagGameover = true;},this);
}
-----------> and THE END (or supposed to be the end)
if(!flagGameover && !star.visible && idx_bullet < bullet_array.length)
initBullet();
else{
if(flagGameover){
console.log("GOTO GAMEOVER");
window.location = "../endgame.html";
}
}
}
}
So is it possible to kill the timer at the end of 21 seconds and everything else with it and just have the player go to endgame.html ????????? i didn't mention the phaser console.log - can it be done that way? Anyone do something like this before, basically a javascript salvage operation.
I found out what it was.
It was missing a callover, as in:
var callGameover = false;
and......
if(flagGameover){
if(callGameover == false){
callGameover = true;
console.log("GOTO GAMEOVER");
window.location = "endgame.html";
I didn't know that was enough to cause an "infinite loop" but apparently it was. Put the above in and loop gone!
I'm writing a Chrome extension that scrolls & listens for newly added child nodes to a parent node.
It then waits a random amount of time, then scrolls down again if more children are added, and stops if in the next 5 seconds nothing appears via ajax (when the list of results has been exhausted, for example).
My question is how I should handle waiting variable amounts of time between each event scroll reaction.
I'd like for it to work politely (yet not fail to scroll down if all 50 elements are loaded at once and the scrolls generated aren't quite enough to get to the next ajax load point).
Any ideas or ways I should think about this? (This is for a totally benign use case btw)
A good solution is a little tricky, but totally works:
var canPoll = true;
var timeout = ... whatever ...; // if we want an absolute timeout
var startTime = (new Date()).getTime();
function randomWait(t) {
// ... waits a random period of time
}
function isDone() {
// ... check if done. returns boolean
}
function complete() {
// ... run when everything is done
}
(function recursive() {
// check for our absolute walltime timeout
canPoll = ((new Date).getTime() - startTime) <= timeout;
// check other conditions too
if (!fn() && canPoll) {
// repeat!
setTimeout(recursive, randomWait(interval));
} else {
// we're done
complete();
}
})();
Adapted from this wonderful answer.
I have a jQuery Mobile web app which targets iOS and Android devices. A component of the application is a background task, which periodically checks for a.) changes to local data and b.) connectivity to the server. If both are true, the task pushes the changes.
I'm using a simple setTimeout()-based function to execute this task. Each failure or success condition calls setTimeout() on the background task, ensuring that it runs on 30 second intervals. I update a status div with the timestamp of the last task runtime for debugging purposes.
In any desktop browser, this works just fine; however, on iOS or Android, after some period of time, the task stops executing. I'm wondering if this is related to the power conservation settings of the devices--when iOS enters stand-by, does it terminate JavaScript execution? That is what appears to happen.
If so, what is the best way to resume? Is there an on-wake event which I can hook into? If not, what other options are there which don't involve hooking into events dependent on user interaction (I don't want to bind the entire page to a click event just to restart the background task).
Looks like Javascript execution is paused on MobileSafari when the browser page isn't focused. It also seems if setInterval() events are late, they are simply fired as soon as the browser is focused. This means we should be able to keep a setInterval() running, and assume the browser lost/regained focus if the setInterval function took much longer than usual.
This code alerts after switching back from a browser tab, after switching back from another app, and after resuming from sleep. If you set your threshold a bit longer than your setTimeout(), you can assume your timeout wouldn't finish if this fires.
If you wanted to stay on the safe side: you could save your timeout ID (returned by setTimeout) and set this to a shorter threshold than your timeout, then run clearTimeout() and setTimeout() again if this fires.
<script type="text/javascript">
var lastCheck = 0;
function sleepCheck() {
var now = new Date().getTime();
var diff = now - lastCheck;
if (diff > 3000) {
alert('took ' + diff + 'ms');
}
lastCheck = now;
}
window.onload = function() {
lastCheck = new Date().getTime();
setInterval(sleepCheck, 1000);
}
</script>
Edit: It appears this can sometimes trigger more than once in a row on resume, so you'd need to handle that somehow. (After letting my android browser sleep all night, it woke up to two alert()s. I bet Javascript got resumed at some arbitrary time before fully sleeping.)
I tested on Android 2.2 and the latest iOS - they both alert as soon as you resume from sleep.
When the user switches to another app or the screen sleeps, timers seem to pause until the user switches back to the app (or when the screen awakens).
Phonegap has a resume event you can listen to instead of polling for state (as well as a pause event if you want to do things before it is out of focus). You start listening to it after deviceReady fires.
document.addEventListener("deviceready", function () {
// do something when the app awakens
document.addEventListener('resume', function () {
// re-create a timer.
// ...
}, false);
}, false);
I use angular with phonegap and I have a service implemented that manages a certain timeout for me but basically you could create an object that sets the timer, cancels the timer and most importantly, updates the timer (update is what is called during the 'resume' event).
In angular I have a scopes and root scope that I can attach data to, my timeout is global so I attach it to root scope but for the purpose of this example, I'll simply attach it to the document object. I don't condone that because you need should apply it to some sort of scope or namespace.
var timeoutManager = function () {
return {
setTimer: function (expiresMsecs) {
document.timerData = {
timerId: setTimeout(function () {
timeoutCallback();
},
expiresMsecs),
totalDurationMsecs: expiresMsecs,
expirationDate: new Date(Date.now() += expiresMsecs)
};
},
updateTimer: function () {
if (document.timerData) {
//
// Calculate the msecs remaining so it can be used to set a new timer.
//
var timerMsecs = document.timerData.expirationDate - new Date();
//
// Kill the previous timer because a new one needs to be set or the callback
// needs to be fired.
//
this.cancelTimer();
if (timerMsecs > 0) {
this.setTimer(timerMsecs);
} else {
timeoutCallback();
}
}
},
cancelTimer: function () {
if (document.timerData && document.timerData.timerId) {
clearTimeout(document.timerData.timerId);
document.timerData = null;
}
}
};
};
You could have the manager function take a millisecond parameter instead of passing it into set, but again this is modeled somewhat after the angular service I wrote. The operations should be clear and concise enough to do something with them and add them to your own app.
var timeoutCallback = function () { console.log('timer fired!'); };
var manager = timeoutManager();
manager.setTimer(20000);
You will want to update the timer once you get the resume event in your event listener, like so:
// do something when the app awakens
document.addEventListener('resume', function () {
var manager = timeoutManager();
manager.updateTimer();
}, false);
The timeout manager also has cancelTimer() which can be used to kill the timer at any time.
You can use this class github.com/mustafah/background-timer based on #jlafay answer , where you can use as follow:
coffeescript
timer = new BackgroundTimer 10 * 1000, ->
# This callback will be called after 10 seconds
console.log 'finished'
timer.enableTicking 1000, (remaining) ->
# This callback will get called every second (1000 millisecond) till the timer ends
console.log remaining
timer.start()
javascript
timer = new BackgroundTimer(10 * 1000, function() {
// This callback will be called after 10 seconds
console.log("finished");
});
timer.enableTicking(1000, function(remaining) {
// This callback will get called every second (1000 millisecond) till the timer ends
console.log(remaining);
});
timer.start();
Hope it helps, Thank you ...
You should use the Page Visibility API (MDN) which is supported just about everywhere. It can detect if a page or tab has become visible again and you can then resume your timeouts or carry out some actions.