This is a follow up question. I have script that I though would work. This script delays my script from running similar to setTimeout. I can't use setTimeout as my software/platform doesn't allow it. So i am looking for a workaround.
function sleep(milliseconds) {
var start = new Date().getTime();
for (var i = 0; i < 1e7; i++) {
if ((new Date().getTime() - start) > milliseconds) {
break;
}
}
}
This function delays the script but it also delays all background scripts from running as well. Im trying to delay my script from running so that all background scripts can finish running. The fact that the above stops all scripts from running defeats the purpose and does not alter to execution order.
I need a script that will delay a block of script without delaying all other background scripts so that they can finish running. Please note that my software/platform does not allow setTimeout and that is why I am looking for a workaround. Thanks.
Related
I am doing a Web Scraping Project with Youtube. I need to automatically scroll to the bottom via Javascript. I am inputting the command in Google Chrome Inspect Console Terminal.
This link: https://stackoverflow.com/a/29971996/3326078 showed me to use this:
window.scrollTo(0, document.body.scrollHeight ||document.documentElement.scrollHeight);
Above works as expected.
However when I do,
for (var i = 0; i < 5; i++) {
setTimeout(window.scrollTo(0, document.body.scrollHeight
|| document.documentElement.scrollHeight),5)}
it doesn't repeat more than once. Can someone explain to me whether what I am trying to do is possible. Again, I am inputting this command in the Google Chrome Inspector Console.
Thanks!
A for loop isn't going to wait for setTimeout to complete before it continues running. If you want the scrolling to wait for setTimeout to finish and run a certain amount of times, you'll need a function that calls the timeout and the calling function again inside of it.
Many ways to do this, but here is my implementation:
var scrollToBottomWithTimeout = function (param_iterations, param_wait_time) {
setTimeout(function () {
window.scrollTo(0, document.body.scrollHeight || document.documentElement.scrollHeight);
if (param_iterations > 0) {
param_iterations = param_iterations - 1;
scrollToBottomWithTimeout(param_iterations, param_wait_time);
}
}, param_wait_time);
};
Once declared, this function can be called like so:
scrollToBottomWithTimeout(5, 1000);
The param_wait_time in milliseconds (thousandths of a second).
Say I have 20 rows of JS code and I want the interpreter to execute only half of the code (<11 rows), then stop, without functions and returns, or without commenting the rest of the code (I already tried a return, see in advance).
A location.reload(true); in row 10 is a close solution but I'm looking for a client side stop.
My question
Is there like a stop command (or functionality) in JavaScript, that asks the interpreter to stop and behave as if no code ran so far?
Why I ask
The background for this question is a problem I have calling a function in more than one keydown event.
Given the keydown event is triggered only once, I consider sending the interpreter back to the start after the keydown event was triggered disposably, and without refreshing the page (Sorry if it seems absurd, I'm new to JS and failed finding the source of the bug).
Of course, the above question is different than the question "why does the keydown event triggered only once", which I already asked here - here's a link for context.
Preventing an XY problem
On one hand, I want to make sure there is no XY problem. On the other hand, I am not allowed to copywrite the previous question to this session hence linked to it above.
Either way, I would be glad to know if what I just described (client side stop of a JS interpreter) is even possible in the current release of the language.
Note: I decided to carefully rewrite the question after some comments earlier today (there were no answers) and did my best ensuring the question is informative and communal.
There is no stop command, but I experienced the need of it before when there was a long-running client-side operation.
The solution:
1) Divide the problem into small packets
2) Make sure you are able to make your function work only for activeMilliseconds milliseconds:
function doStuff(packets, currentIndex, activeMilliseconds) {
var start = new Date(); //Start of chunk
while((currentIndex < packets.length) && (new Date() - start < activeMilliseconds)) {
//Do something with packets[currentIndex]
currentIndex++;
}
return currentIndex;
}
3) Now that we are able to work for activeMilliseconds milliseconds, we need to use this asynchronously:
//Define packets
var currentIndex = 0;
var intervalID = setTimeout(function() {
If(currentIndex = doStuff(packets, currentIndex, activeMilliseconds) >= packets.length) clearInterval(intervalID);
}, totalMilliseconds);
Node: totalMilliseconds > activeMilliseconds should be true. For example, if totalMilliseconds is 250, and activeMilliseconds is 200, then in each 250 milliseconds a chunk will run for 200 milliseconds, leaving the browser to do its stuff for 50 milliseconds every 250 milliseconds even if there is a lot of work to do.
4) Make sure a job stops a previous similar job:
function doJob(packets, intervalID, activeMilliseconds, totalMilliseconds) {
clearInterval(intervalID);
//Define packets
var currentIndex = 0;
var intervalID = setTimeout(function() {
If(currentIndex = doStuff(packets, currentIndex, activeMilliseconds) >= packets.length) clearInterval(intervalID);
return intervalID;
}, totalMilliseconds);
}
If you use this idea for your key event, then it will stop the previous keyboard, your maximum wait time to do so will be activeMilliseconds, which is an acceptable compromise in my opinion.
That said, this methodology should be only used in the case when you have no other option. You need to know that Javascript has a single thread, so even if you trigger a function execution while a previous instance of the event is still running, your new event will sequentially be executed when the other event is finished.
I planned to use setInterval to simply set a variable to false, which would be inspected by the main loop to stop. Example (note: this is an example only, the acutal code is not a while() loop which would be easy to reconstruct, but a quite complex, and long to execute script generated by a closed source software actually):
var running = true;
setInterval(function () {
if (running) {
console.log("Stopping now!");
running = false;
}
}, 100);
while (running) {
// do something ...
}
However it does not seem to work at least firefox drops a "busy script" box after a while. What's the problem with the code above? setInterval() may not be able to run if your script already runs otherwise? I couldn't find an exact specification what setInterval() does exactly.
I would need something like this, since I already have huge (and very long to execute) script, so I thought I will try to stop it after a while, then using setTimeout() to let the browser breath a bit and then continue: as the script itself does know its internal state so it can continue from any point, but it's not an option to modify the script actually ....
If it's not possible with setInterval, is there any alternative to this, without any modification in the "long to execute" code itself?
Thanks!
If it's not possible with setInterval, is there any alternative to this, without any modification in the "long to execute" code itself?
One possibility is to make that a web worker rather than trying to use it on the UI thread. Despite people repeatedly saying so, JavaScript is not single-threaded (JavaScript, the language, is silent on the subject), not even on browsers anymore. In the browser environment, there is one main UI thread, but you can spawn other worker threads (web workers). The worker(s) and the main UI code can communicate via postMessage / onmessage.
Here's an example of a web worker in action. This page uses JavaScript on the UI thread to start a web worker, which runs on a separate thread. The worker runs for 10 seconds, busily updating a counter (this is just to simulate a long-running, calculation-intensive process), and sends updates to the UI thread every second:
Main page:
<!doctype html>
<html>
<head>
<meta charset="UTF-8">
<title>Worker Example</title>
<style type="text/css">
body {
font-family: sans-serif;
}
</style>
<meta charset="UTF-8">
</head>
<body>
<script>
(function() {
var worker = new Worker("worker.js");
worker.onmessage = function(e) {
display("Worker says " + e.data);
};
display("Starting worker");
worker.postMessage("start");
function display(msg) {
var p = document.createElement('p');
p.innerHTML = String(msg);
document.body.appendChild(p);
}
})();
</script>
</body>
</html>
worker.js:
this.onmessage = function(e) {
var counter, lastUpdate, now;
if (e.data === "start") {
// Loop without yeilding for 10 seconds, sending updates
// to the UI every second.
start = lastUpdate = Date.now();
counter = 0;
do {
++counter;
now = Date.now();
if (now - lastUpdate > 1000) {
lastUpdate = now;
this.postMessage(counter);
}
}
while (now - start < 10000);
this.postMessage("Done");
}
};
(You're not required to make the worker wait for a message to start, but it's fairly common.)
The problem is that Javascript is single-threaded. Rewrite your while loop to use setInterval itself and everything should work, since you will release the thread at the end of each loop.
You should use setTimeout or setInterval instead while loop. JS runs in single thread, so infinite loop will freeze your browser.
var running = true;
setInterval(function(){
if(running){
console.log('Stopping now!');
running = false;
}
}, 100);
(function loop(){
// Do yours loop stuff
if( running ){
setTimeout(loop, 0);
}
})();
You should consider using Worker or writing asynchronous code.
Or you can modify your code.
var running = true;
var past = Date.now();
while (running) {
// do heavy calculations ...
if ((Date.now() - past) > 10) {
running = false;
}
}
Of course, blocking loops aren't good idea, but I don't see good way to satisfy requirement:
If it's not possible with setInterval, is there any alternative to this, without any modification in the "long to execute" code itself?
JavaScript runs in a single threaded event loop. What this means is while your code is running no other code can run. This is why your callback does not get executed.
You can workaround this by also making your while(running) be asynchronous. Consider doing the following:
var running = true;
var monitor = setInterval(function () {
if (running) {
console.log("Stopping now!");
running = false;
clearInterval(monitor);
}
}, 100);
var work = setInterval(function() {
if (running) {
// do something
} else {
clearInterval(work);
}
}, 1);
Don't forget to call clearInterval!
In this code, why does the css change not complete until the while loop finishes? I know a loop hangs the browser but I would have thought the css change would be synchronous and therefore finish before the while loop even starts.
Bonus Question: Is there any way for me to get that css change to complete before moving to the while loop without giving up control of the javascript thread?
function run() {
var then = +new Date()
, now
;
$('#mydiv').css('display','block');
now = + new Date();
while (now - then < 5000) {
now = +new Date();
}
}
fiddle: http://jsfiddle.net/ezVZT/2/
Browsers don't always update the page immediately. They'll often wait with updates rendered but not painted while scripts execute and batch all the repainting together.
In your code you're applying a change to the styling, but then executing a 5 second loop which will block everything. The CSS change just has to wait.
If you need to wait five seconds before doing something use a setTimeout() call, or since you're using jQuery, look at .delay().
Would something like $elem.addClass('xyz') solve your problem? you can then assign the display: block to your class in css. Maybe that would be faster?
Try the following code. It might work.
function run() {
var then = +new Date()
, now
;
$('#mydiv').css('display','block');
$("#mydiv", window.parent.document).load($("mydiv").html());
now = + new Date();
while (now - then < 5000) {
now = +new Date();
}
}
function reset() {
$('#mydiv').css('display','none');
}
I want to use a timer as a fallback in case I end up in an infinite loop. It seems that set interval is the right way to do this. However, it's not working for me.
From my research, it seems like setInterval should run in a separate thread in the background, but I don't see it.
Why is this behavior happening? And how do I solve this?
var time = 0;
window.setInterval(function(){time++;}, 1000);
while (true) {
//stuff done
if (time >= 5) {
break;
}
}
Browser javascript runs in a single thread. So if you perform something that takes too long - it will freeze browser.
See John Resig article for further details: http://ejohn.org/blog/how-javascript-timers-work/
After you read that article you'll get that your setInterval callback queued to be run in 1000ms after now but only after the current code is finished. It cannot finish though, because of the infinite loop.
zerkms has the correct answer. But I would add that web workers are a way to get some multi-threaded-ish behavior from client side javascript.
var worker = new Worker('my_task.js');
worker.onmessage = function(event) {
console.log("Called back by the worker!\n");
};
The worker runs in a background thread, and you can exchange messages and subscribe to events. It's pretty nifty.
As has been already said - the callback to setInterval doesn't run until the infinite loop finishes. To do what you are trying to achieve - without using web workers - you have to check the time from the loop itself:
var start = Date.now();
while((Date.now() - start) < 5000){
...
}