Is it possible to increase the time out limit for JavaScript?
If I have a script that executes for more than 20/30 seconds chrome will pop-up with the unresponsable page dialog.
Making a more efficient script won't help me because the script sometimes need to iterate through a function for a million or billion times
To split the function on steps/chunks and run those inside setInterval(function(){}).
This way page will be responsive, you will be able to notify user about progress of execution and you will get your job done.
UPDATE: Here is simple function that takes
worker function executing each iteration,
chunksz - number of iteration running in single chunk
maxit - total number of iterations.
function task(worker, chunksz, maxit)
{
var idx = 0;
var xint = null;
function exec_chunk()
{
for(var n = 0; n < chunksz; ++n)
{
if(idx >= maxit) { return; }
worker(idx++);
}
setTimeout(exec_chunk,1);
}
exec_chunk();
}
Here is an example : http://jsfiddle.net/Ed9wL/
As you see you get all iterations in order.
UPDATE2:
Say you have a loop:
for(var i=0; i<100000; ++i) { ... do something ... }
then you need to wrap body of the loop into a function and call the task above with it like this:
task(function(i){ ... do something ... },100, 100000);
or like this:
function loopBody(i){ ... do something ... }
task(loopBody,100, 100000);
When you have lots of processing to do client side, you need to split out your work into separate threads. The browser only has a single thread for handling user input (events) and for processing JS. If you're processing too much JS, without yielding, the UI becomes unresponsive and the browser is not happy.
How can you allow your script to yield? The new way is to use web workers http://www.whatwg.org/specs/web-workers/current-work/ . This works by creating a separate thread to run your JS, thread thread does not access to the DOM and can be run concurrently.
However, this newer technology doesn't exist in all browsers. For older browsers, you can split up your work by having the script call itself through timeouts. Whenever a timeout occurs, the script is yielding to the browser to run its events, once the browser is done, your next timeout will be triggered.
Example http://jsfiddle.net/mendesjuan/PucXf/
var list = [];
for (var i = 0; i < 500000; i++) {
list.push(Math.random());
}
function sumOfSquares(list) {
var total = 0;
for (var i = 0; i < list.length; i++) {
total += list[i] * list[i];
// DOM manipulation to make it take longer
var node = document.createElement("div");
node.innerHTML = "Sync temp value = " + total;
document.body.appendChild(node);
}
return total;
}
function sumOfSquaresAsync(arr, callback) {
var chunkSize = 1000; // Do 1000 at a time
var arrLen = arr.length;
var index = 0;
var total = 0;
nextStep();
function nextStep() {
var step = 0;
while (step < chunkSize && index < arrLen) {
total += arr[index] * arr[index];
// DOM manipulation to make it take longer
var node = document.createElement("div");
node.innerHTML = "Async temp value = " + total;
document.body.appendChild(node);
index++;
step++;
}
if (index < arrLen) {
setTimeout(nextStep, 10);
} else {
callback(total);
}
}
}
sumOfSquaresAsync(list, function(total) {console.log("Async Result: " + total)});
//console.log("Sync result" + sumOfSquares(list));
The example on jsfiddle has the synchronous call commented out, you can put it back in to see the browser come to a crawl. Notice that the asynchronous call does take a long time to complete, but it doesn't cause the long running script message and it lets you interact with the page while calculating (select text, button hover effects). You can see it printing partial results on the pane to the bottom right.
UPDATE http://jsfiddle.net/mendesjuan/PucXf/8/
Let's try to use c-smile's task function to implement sum of squares. I think he's missing a parameter, a function to call back when the task is finished. Using task allows us to create multiple chunked functions without duplicating the work of calling setTimeout and iteration.
/**
* #param {function} worker. It is passed two values, the current array index,
* and the item at that index
* #param {array} list Items to be traversed
* #param {callback} The function to call when iteration is finished;
* #param {number} maxit The number of iterations of the loop to run
* before yielding, defaults to 1000
*/
function task(worker, list, callback, maxit)
{
maxit = maxit || 1000;
var idx = 0;
exec_chunk();
function exec_chunk()
{
for(var n = 0; n < maxit; ++n)
{
if(idx >= list.length) {
callback();
return;
}
worker(idx, list[idx]);
idx++;
}
setTimeout(exec_chunk,1);
}
}
function sumOfSquaresAsync(list, callback)
{
var total = 0;
// The function that does the adding and squaring
function squareAndAdd(index, item) {
total += item * item;
// DOM manipulation to make it take longer and to see progress
var node = document.createElement("div");
node.innerHTML = "Async temp value = " + total;
document.body.appendChild(node);
}
// Let the caller know what the result is when iteration is finished
function onFinish() {
callback(total);
}
task(squareAndAdd, list, onFinish);
}
var list = [];
for (var i = 0; i < 100000; i++) {
list.push(Math.random());
}
sumOfSquaresAsync(list, function(total) {
console.log("Sum of Squares is " + total);
})
If your goal is to suppress "Kill-Wait" message as quick temporary fix for your slow JavaScript then the solution is to open Tools/Developer Tools in Google Chrome and keep it open and minimized somewhere on your desktop while browsing .
Related
I have four different button effects where each effect are declared in a variable.
Therefore, I bring all of these four variables and place them within an array called arr in which is used in the clickByItself() function using Math.floor(Math.random()) methods.
Without the for loop, the code clicks by itself randomly in one of the four buttons every time I reload the page.
function clickByItself() {
let random = Math.floor(Math.random() * arr.length);
$(arr[random]).click();
}
However, using the for loop I am not being able to make these clicks one-by-one within the maximum of 10 times.
var blueButtonEffect = code effect here;
var redButtonEffect = code effect here;
var greenButtonEffect = code effect here;
var yellowButtonEffect = code effect here;
var arr = [blueButtonEffect, redButtonEffect, greenButtonEffect, yellowButtonEffect];
//will click on buttons randomly
function clickByItself() {
let random = Math.floor(Math.random() * arr.length)
var i;
for (i = 0; i < 10; i++) {
$(arr[random]).click();
setTimeout(clickByItself(), 1000);
}
}
The final output with the current code above is the four buttons being clicked at the same time, not one-by-one.
So, how can I have this function to press a random button by 10 times one-by-one with one second of interval from each click?
To fix your code you need:
A base case for your recursion
Pass a function reference to setTimeout. Currently, you are executing clickByItself and passing its return value (which is undefined) to setTimeout.
Do not use setTimeout in a loop without increasing the time by a factor of i, as the for loop will queue all the function calls at the same time
Alternatively, you can use a "times" argument to avoid looping
You could try something like
function clickByItself(times = 0) {
let random = Math.floor(Math.random() * arr.length)
$(arr[random]).click();
if (++times < 10) {
setTimeout(function(){clickByItself(times);}, 1000);
}
}
An example with console logs
https://jsfiddle.net/pfsrLwh3/
The problem is that the for loop calls the setTimeout 10 times very quickly. If you want to wait until the previous function call finishes prior to calling the next, then you should use recursion or just use a setInterval.
Recursion:
function clickByItself(numIterations) {
let random = Math.floor(Math.random() * arr.length)
let i;
$(arr[random]).click();
if( numIterations < 10 ){
setTimeout(() => {
clickByItself(numIterations += 1)
}, 1000)
}
}
clickByItself(0);
With setInterval
let numIterations = 0;
function clickByItself() {
let random = Math.floor(Math.random() * arr.length);
let i;
$(arr[random]).click();
numIterations += 1;
if( numIterations > 10) {
clearInterval(loop)
}
}
let loop = setInterval(test2, 1000);
Are you saying this is working for only 4 times but I think your above code will run in an infinite loop as you are calling clickByItself() again in the for loop.
If you want press a random button by 10 times one-by-one with one second of interval from each click then replace the for loop with
for (i = 0; i < 10; i++) {
setTimeout($(arr[random]).click(), 1000);
}
I have a huge collection coming from server, I need to create DOM elements for each row from the collection.
If I try to append the items to DOM using a for loop, my browser hangs for some time. To avoid this I tried adding the items in chunks using setTimeout, but until all the chunks are processed, the items are not appearing in the UI.
If I add a dubugger in the code, for each iteration I can see it appearing in the DOM.
I have created a sample to simulate this.
var longArray = [];
for (var i = 0; i < 10000; i++) {
var item = {
"Name": "item" + i,
"Id": i
};
longArray.push(item);
}
var chunks = _.chunk(longArray, 2);
for (var i = 0; i < chunks.length; i++) {
(function(index) {
setTimeout(addItem(chunks[index]), 1000);
})(i);
}
function addItem(items) {
_.forEach(items, function(item) {
$("<li />").html(item.Id + " : " + item.Name).appendTo("ul");
});
}
<script src="https://code.jquery.com/jquery-3.3.1.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.5/lodash.js"></script>
<ul></ul>
For demo, I added 1 second delay, I am expecting before the next execution of the function, the previously added item to appear on the screen.
Currently items only appearing on the screen when all the items are processed.
I am not going tell much detail because you will get many related topics here. Just be understood that there is a logical issue in your code, basically your all setTimeouts are running all together after 1000ms. For example, change your code as follows, it would work expectedly.
var longArray = [];
for (var i = 0; i < 10000; i++) {
var item = {
"Name": "item" + i,
"Id": i
};
longArray.push(item);
}
var chunks = _.chunk(longArray, 2);
function addItem() {
var items = chunks.pop();
_.forEach(items, function(item) {
$("<li />").html(item.Id + " : " + item.Name).appendTo("ul");
});
if (chunks.length) {
setTimeout(addItem, 1000);
}
}
addItem();
Moreover you can use requestAnimationFrame instead of setTimeout, that will fix your freezing issue and will work better than setTimeout
I'm trying to make a faux loading screen, and I need delays between loading messages of about 20-50ms or so so that people can actually see what's going on before it cuts to the initialized screen. The button that activates this goes to the following function:
function gameinit() {
for (k = 0; k <=1; k += 0.125) {
setTimeout(function () {
var nexttxt = "Loading... " + toString(100 * k) + "%"
}, 20);
displayupdate(nexttxt);
}
}
However this comes up as an incorrect syntax (on JSfiddle - https://jsfiddle.net/YoshiBoy13/xLn7wbg6/2/) when I use JShint - specifically lines four and five. I've looked at the guides for this and everything seems to be in order. What am I doing wrong?
(Note: displayupdate(nexttxt) updates the <p> tags with the next line of text)
When executing the script, nothing happens - the sixteen lines of text on the HTML move up as normal, the top eight being replaced with the eight generated by the gameinit() function, but the gameinit() only generates blank. If the script is executed again, it just outputs eight lines of 112.5% (as if it was the 9th iteration of the for loop).
I'm almost certain it's something elementary that I've missed, could someone please tell me what I've done wrong?
Use setInterval() instead, you can clear interval using clearInterval()
function gameinit() {
displayupdate("Loading... 0%");
var k = 0;
var inter = setInterval(function() {
if (k < 1) {
k += .25;
displayupdate("Loading... " + 100 * k + "%")
} else {
clearInterval(inter);
}
}, 2000);
}
function displayupdate(d) {
console.log(d);
}
gameinit();
here is another function can do this better ---- setInterval
var txt = '';
var time = 0;
var id = setInterval(function(){
console.log("loading..."+time/8*100+"%");
if(time++>7)
clearInterval(id);
},1000);
setTimeout doesn't work as you would expect it to work inside loops. You have to create a closure for each loop variable passed on to setTimeout, or create a new function to execute the setTimeout operation.
function gameinit() {
for (var k = 0; k <= 1; k += 0.125) {
doSetTimeOut(k);
}
}
function doSetTimeOut(k) {
setTimeout(function() {
var nexttxt = "Loading... " + toString(100 * k) + "%"
}, 20);
displayupdate(nexttxt);
}
I have created an utterly simple blackjack game that stores the first value of a shuffled array of cards into corresponding players' arrays, dealing them as actual hands. For some odd reason, I can't seem to find a way to execute the core part of the code multiple times without getting an infinite loop. For the time being, I have only tried running the quite commonplace "for" command which is meant for multiple statements, but just doesn't seem to work here.
The programm on its primitive form is as follows...
var dealerCards = [];
var playerCards = [];
var firstDeck = [];
function shuffle(o){
for(var j, x, i = o.length; i; j = Math.floor(Math.random() * i), x = o[--i], o[i] = o[j], o[j] = x);
return o;
}
function createShuffledDeckNumber(array, x) {
for (i = 0; i < 4*x; i++) {
array.push(1,2,3,4,5,6,7,8,9,10,11,12,13);
}
shuffle(array);
}
function drawCard(playersHand, playerSoft, playerHard) {
playersHand.push(firstDeck[0]);
firstDeck.shift();
}
function checkDeckDrawOne(playersHand) {
if (firstDeck.length === 0) {
createShuffledDeckNumber(firstDeck, 1);
drawCard(playersHand);
}else{
for (i = 0; i < 1; i++) {
drawCard(playersHand);
}
}
}
for (i = 0; i < 4; i++) {
dealerCards = [];
playerCards = [];
checkDeckDrawOne(dealerCards);
checkDeckDrawOne(dealerCards);
checkDeckDrawOne(playerCards);
checkDeckDrawOne(playerCards);
console.log("dealerCards",dealerCards,"playerCards",playerCards);
console.log("firstDeckDrawn", firstDeck, "Number", firstDeck.length);
}
Additional Notes;
The presumed objective could be performing calculations to figure out the winner by imitating the effect of consecutive computing rounds based on a finite number of values stored in each player's array. Although, I've tried a seried of different things when it comes to emulating the real life circumstances of actually playing blackjack, this version seems to do just that, by also giving the programmer the ability to introduce counting systems like KO or HiLo. The main logic behind the whole thing is fairly simple; order x shuffled decks whenever a command that involves drawing a card is being executed unless the deck has at least one card.
It's rather fair to ponder why should I possibly bother creating multiple rounds in such a game. Reason is, that I want to create an autoplayer application that provides me with percentages on processed data.
Your variable i in function checkDeckDrawOne() has global scope, meaning it alters the value of i in the main loop:
for (i = 0; i < 4; i++) {
dealerCards = [];
playerCards = [];
checkDeckDrawOne(dealerCards);
checkDeckDrawOne(dealerCards);
checkDeckDrawOne(playerCards);
checkDeckDrawOne(playerCards);
console.log("dealerCards",dealerCards,"playerCards",playerCards);
console.log("firstDeckDrawn", firstDeck, "Number", firstDeck.length);
}
Change this:
for (i = 0; i < 1; i++) {
drawCard(playersHand);
}
to this:
for (var i = 0; i < 1; i++) {
drawCard(playersHand);
}
although why you need a loop here anyway is baffling.
My Javascript timer is for people with a rubiks cube with generates a scramble (nevermind all this, but just to tell you I'm generating after each solve a new scramble will be generated) and my scrambles do actually have a while (true) statement. So that does crash my script, but it 95/100 times stops just before the script crashes but I don't wanna have any times.
Let me explain a bit more detailed about the problem.
Problem: javascript crashes because my script takes too long to generate a scramble.
Below you have 3 functions I use.
This function generates a scramble with the Fisher-Yates shuffle.
Timer.prototype.generateScramble = function(array) {
for (var i = array.length - 1; i > 0; i--) {
var j = Math.floor(Math.random() * (i + 1));
var temp = array[i];
array[i] = array[j];
array[j] = temp;
}
return array;
};
This function validates the input e.g. I receive an array as the following:
Here I only have to check the first character. That's why I use the seconds [ ] notation. I don't want people get an F with an F2 e.g.
var scr = ["F","R","U","B","L","D","F2","R2","U2","B2","L2","D2","F'","R'","U'","B'","L'","D'"]
Timer.prototype.validateScramble2 = function(array) {
var last = array.length-1;
for (var i = 0; i < array.length-1; i++) {
if (array[i][0] == array[i+1][0]) {
return false;
}
}
for (var i = 0; i < array.length-2; i++) {
if (array[i][0] == array[i+2][0]) {
return false;
}
}
if (array[0][0] == [last][0]) {
return false;
}
return true;
};
The above functions are just waiting to be called. Well in the function below I use them.
Timer.prototype.updateScramble2 = function(scrambleArr, len, type) {
var self = this;
var scramble = '', j, updatedArr = [];
while (updatedArr.length < len) {
j = (Math.floor(Math.random() * scrambleArr.length));
updatedArr.push(scrambleArr[j]);
}
while (!self.validateScramble2(updatedArr)) {
updatedArr = self.generateScramble(updatedArr);
}
for (var i = 0; i < updatedArr.length; i++) {
scramble += updatedArr[i] + ' ';
}
scrambleDiv.innerHTML = scramble;
};
I assume you guys understand it but let me explain it briefly.
The first while-loop adds a random value from the given array(scrambleArr) into a new array called updatedArr.
The next while-loop calls the validateScramble2() function if there isn't in an array F next to an F2.
The for-loop adds them into a new variable added with a whitespace and then later we show the scramble in the div: scrambleDiv.innerHTML = scramble;
What do I need know after all this information?
Well I wanna know why my updateScramble2() functions lets my browser crash every time and what I do wrong and how I should do it.
I'm not entirely sure I understand the question, but from the way your code looks, I think you have an infinite loop going on. It appears as if validateScramble2 always returns false which causes your second loop in updateScramble2 to perpetually run.
I suggest you insert a breakpoint in your code and inspect the values. You could also insert debugger; statements in your code, works the same way. Open dev tools prior to doing these.
A suggestion is instead of using loops, use a timer. This breaks up your loop into asynchronous iterations rather than synchronous. This allows the browser breathing space for other operations. Here's an example of a forEachAsync:
function forEachAsync(array, callback){
var i = 0;
var timer = setInterval(function(){
callback.call(null, array[i]);
if(++i >= array.length) clearInterval(timer);
}, 0);
}
forEachAsync([1,2,4], function(item){
console.log(item);
});
You can take this further and use Promises instead of callbacks.