I am using a simple function that before it run a slow function, it put a text in the page with : "loading". But this text only show after the function finish.
I did try to add a css too, but it only run after the function finish.
I put a exemple on fiddle and replace my big function with a sleep function. and the result is the same.
http://jsfiddle.net/9nzg9f6L/5/
here the code I am using in the html page:
<div id='msg'>BEGIN</div>
<div style="cursor: pointer; color:red;" id='clickme'>click me!</div>
here is the javascript code:
$("#clickme").click(function(){
// this code only run after the sleep ...
$('#msg').text("PROCESSING");
console.log("text changed");
sleep(5000);
console.log("END function");
});
function sleep(miliseconds) {
var currentTime = new Date().getTime();
while (currentTime + miliseconds >= new Date().getTime()) {}
}
I cannot modify the sleep(5000) function. It is a placeholder for proprietary and obfuscated function.
even this:
http://jsfiddle.net/9nzg9f6L/7/
does not work.
Take a look at my updated jsFiddle which solves your problem.
http://jsfiddle.net/9nzg9f6L/10/
Updated Code
$("#clickme").click(function(){
//Call slow func using a Self Executing func.
(function(){
//Create the deferred object to use
var defObj1 = $.Deferred();
//Call you slow func
$('#msg').text("PROCESSING");
setTimeout(function(){
sleep(5000)
defObj1.resolve();
}, 500);
return defObj1;
})().done(FunctionTwo);
})
//We call this once the Sleep function is done.
var FunctionTwo = function(){
$('#msg').text("FUNCTION ONE COMPLETED OK..");
};
//Make it slow....
function sleep(miliseconds) {
var currentTime = new Date().getTime();
while (currentTime + miliseconds >= new Date().getTime()) {}
}
Here we make use of a Self-Executing anonymous function and jQuery's Deferred Object with a setTimeout to ensure the slow function executes without halting the entire app. Once done, we can call Function two which in this case simply outputs that the slow function completed.
The problem here is since javascript is single threaded language, browser just didn't have enough time to alter DOM before "slow" function blocks UI completely. To overcome this situation usually you delay slow operation by some very little time, so that DOM update can finish before bad code starts working:
$("#clickme").click(function(){
$('#msg').text("PROCESSING");
$('#msg').addClass("message");
setTimeout(function() {
sleep(5000);
}, 50);
});
Demo: http://jsfiddle.net/9nzg9f6L/6/
But of course if it is possible to make slow code asynchronous it would be the best approach in this case. Blocking UI is never a good UX. Also look into WebWorkers.
Related
I have a project consisting out of thousands of lines of Javascript code. It contains a lot of complex calculations which are divided into different functions. One function calls three or four other functions, of which one calls another one of those four functions,... Just to say, it has a complex structure.
Some functions also need to do calculations on the DOM. E.g, One functions adds an element to the DOM, and another one needs to do a calculation using the coordinates of that element in the DOM.
However, when the user visits the page, all of this is executed by one function called - in this example - bigFunction.
The problem is that bigFunction starts a long series of complex calculations and function calls resulting in stalling the page loading, and making the page irresponsive until bigFunction is finished.
I already tried simple solutions like defering my .js-file, using $(document).ready(function() { bigFunction(); }), $(window).on('load', function() { bigFunction(); }). None of that changed anything. I tried converting my code to async code but that was crazy work.
I am actually just wondering if there is a way to call bigFunction asynchronous somehow but without converting all of my code to async code and making all my functions async, is that possible?
Some test-code:
// Executing big function
bigFunction();
// "HTML"
console.log("[+] HTML Loaded!!"); // -> Must load async with bigFunction running
// This is a function I execute on a page and which stops the page from
// loading until it's finished. How do I async this?
async function bigFunction() {
console.log("[+] bigFunction started");
if (functionToReturnTrueOrFalseAfterALongCalculation()) {
console.log("WOW!");
}
console.log("[+] bigFunction finished")
}
// Ignore logic in this function... It just returns true of false randomly after 3,5 secs
function functionToReturnTrueOrFalseAfterALongCalculation() {
console.log("[+] functionToReturnTrueOrFalseAfterALongCalculation started");
let to_return = false;
// Give back random value
let random_number = Math.floor(Math.random() * 100) + 1;
if (random_number < 50) {
to_return = true;
}
sleep(3500);
// Just to show that functions are nested deep in each other
doRandomStuff();
console.log("[+] functionToReturnTrueOrFalseAfterALongCalculation ended");
return to_return;
}
// Just another slow function
function doRandomStuff() {
console.log("[+] doRandomStuff started");
sleep(1000);
console.log("[+] doRandomStuff ended");
}
// Ignore
function sleep(milliseconds) {
var start = new Date().getTime();
for (var i = 0; i < 1e7; i++) {
if ((new Date().getTime() - start) > milliseconds){
break;
}
}
}
HTML should load faster...
I am actually just wondering if there is a way to call bigFunction asynchronous somehow but without converting all of my code to async code and making all my functions async, is that possible?
Fundamentally, not really. In order for the page to remain responsive, your JavaScript code must yield control back to the browser regularly. If you have some very expensive JS that blocks for 0.5 seconds, the page will appear to the user to be unresponsive for those 0.5 seconds. (0.5 seconds of blocking is not good. 0.05 seconds of blocking every 0.1 seconds could be better, for example.)
To have the site remain responsive, all of your functions that invoke sleep (the expensive calculations) need to be refactored to stagger up their jobs. Pure JavaScript jobs can be offloaded to a service worker, thereby freeing up resources to keep the active tab responsive, but jobs that require DOM manipulation (some of which you have) can't be offloaded to a service worker.
Without seeing what exactly the expensive functions are doing, more details are difficult, but for what you want, in the end you'll probably need to end up with code that looks something like
async function bigFunction() {
console.log("[+] bigFunction started");
// the below function does not block for long periods of time,
// but only for short periods of time, staggered up
const result = await functionToReturnTrueOrFalseAfterALongCalculation();
if (result) {
console.log("WOW!");
}
console.log("[+] bigFunction finished")
}
The code that comes after the execution of bigFunction would absolutely have to be able to handle the asynchronous execution flow - if you had
doSomething();
bigFunction();
finishDoingStuff();
you would have to refactor to something like
doSomething();
await bigFunction();
finishDoingStuff();
or to
doSomething();
bigFunction(finishDoingStuff);
where bigFunction calls finishDoingStuff once its expensive tasks are complete.
I am trying to figure out why in my Code section, this.sleep(5000) seems to be getting called before my draw function, because it doesn't get drawn to the canvas until after sleep is done. any insights on why this isn't working the way I want it to?
Sleep function:
sleep: function(milliseconds) {
setTimeout(function(){
var start = new Date().getTime();
while ((new Date().getTime() - start) < milliseconds){
// Do nothing
}
},0);
},
Code:
var g = new Graph(this.diagram);
g.DrawPolygons(ctx,"blue");
this.sleep(5000);
Short answer
Don't do it this way. Even if you get it to work, it will be inconsistent, will cause you many problems, and is almost globally considered bad practice.
Long answer
JavaScript runtimes are almost always designed to be asynchronous. Your while loop is intended to make everything... wait. You cannot (or at least shouldn't) do that in most JavaScript environments.
Instead, schedule events/functions to be executed some number of ms in the future. This is what setTimeout is for. This removes the need for a sleep function.
Here's what your code might look like after the changes described above are applied:
var g = new Graph(this.diagram);
g.DrawPolygons(ctx, "blue");
setTimeout(function() {
g.DrawPolygons(ctx, "red"); // Or whatever
setTimeout(function() {
g.DrawPolygons(ctx, "yellow"); // Or whatever
// etc..
}, 5000);
}, 5000);
ES2015 update - using promises
To avoid potential deeply nested setTimeouts, you can use this
const sleep ms = new Promise(resolve => setTimeout(resolve,ms));
which is simply a promise that resolves in ms milliseconds. This allows you to keep everything in one block:
var g = new Graph(this.diagram);
g.DrawPolygons(ctx, "blue");
(async () => {
g.DrawPolygons(ctx, "red");
await sleep(5000);
g.DrawPolygons(ctx, "yellow");
await sleep(5000);
// ...
})()
Note two things:
Under the hood, there are still events/callback. It looks like C's or Python's sleep but behave very differently.
You can only use this inside asynchronous functions. See here for more information.
There are several problems with the code you've posted. First off, you should never use a while loop to halt code execution.
Secondly, you're setting a timeout, which allows other code to be executed in the interim (yes, even if the timeout is zero seconds). Remove that and you should be able to pause execution (BUT DON'T DO THIS):
sleep: function(milliseconds) {
var start = new Date().getTime();
while ((new Date().getTime() - start) < milliseconds){
// Do nothing
}
},
However, occupying the JS thread means that other browser operations (redraws, etc) will be halted until your sleep function exits. Just having this code in your JS file is an antipattern, you'd be better off finding a different way to solve your problem. Read up on the XY problem and ask a new question.
In case all you wanted to do was execute some code after a certain interval without blocking everything else, setTimeout is all you need.
sleep: function(ms, funcToExecute) {
setTimeout(funcToExecute, ms);
},
(Though at this point, sleep is redundant)
This is happening because of how JavaScript's setTimeout works. When you do:
setTimeout(function(){}, 0)
You are not actually telling it to run the function after 0ms (the lowest value is actually 4ms, but that's besides the point). You are telling it to run the function in the future. What it actually does is put the function at "the end of the stack". It'll finish running the function that called it, and maybe even run some UI redraws before it runs the timeout.
If this code is ran in a loop, your timeouts will not run at all when you think they will ;)
Also, remember JavaScript is single threaded. One thread runs your code as well as the UI redraws. Doing a while loop that does nothing and waits for 5 seconds will lock up the browser. It will prevent any user interaction and UI redraws. It might even make the OS think the browser crashed. DO NOT DO THIS!
Instead, try setting a timeout to run the next polygon after 5000ms:
var g = new Graph(this.diagram);
g.DrawPolygons(ctx,"blue");
setTimeout(function(){
// Code to run after the "sleep"
// Maybe another shape
g.DrawPolygons(ctx, "red");
}, 5000);
I'm making a little game, and i was making a character death sequence when I ran into this problem. The
eloop(setInterval(e_seq,100)
plays the ending sequence. After that, I want execution to stop for a second before displaying the score and stuff.
But the current sleep method i'm using pauses the entire execution, including the loop, while I want the loop to be completed before pausing the game for a second.
The place where sleep is called: (inside the main gameloop)
eloop=setInterval(e_seq,100);
sleep(1000);
The sleep method:
function sleep(msec)
{
var time= new Date().getTime();
while(time+msec>= new Date().getTime())
{}
}
any solutions?
PS: calling sleep at the end of the gameloop (inside an if condition checker) was pausing the execution before the gameloop began for some reason....
I think you probably want something more along the lines of
setTimeout(function () { e_seq(); }, 1000);
This would wait one second and then execute the e_seq() function, which I think is the purpose of your code, although it's open to a little interpretation...
Did you try just the setInterval?
setInterval(function(){ ... }, 3000);
i have tried something
var looper;
var looptime = 2000;
var doloop = function(){
console.log("doing this")
}
function begin(callthis){
looper = setInterval(callthis,looptime);
}
function pause(callthis,sleeptime){
clearInterval(looper);
setTimeout(function(){
looper = setInterval(callthis,looptime);
},sleeptime)
}
using like:
begin(doloop);
and pause with
pause(doloop,10000);
You need a callback when using "sleep" functionality. The sleep concept does not exist in JavaScript.
You should not use a busy-loop as you do as that will hold off any other processes as well as JavaScript is single threaded (incl. DOM updates). Use a timer instead but as timers are asynchronous you will have to use the mentioned callback.
It's not so complicated -
Modify the sleep method like this:
function sleep(timeout, callback) {
setTimout(callback, timeout); // or just call this directly...
}
(as you can see it's a bit excess with the wrapper so I would recommend just calling the setTimeout() directly).
Now you can implement your score screen into a function:
function showScores() {
...
}
Then when you want to delay a second before showing the score screen do:
sleep(1000, showScores);
or simply:
setTimeout(showScores, 1000);
Note that the rest of your code will continue after calling this method so make sure all code resides in functions so you can use them as callbacks.
Knowing that while Node.js is working asynchronously, writing something like this:
function sleep() {
var stop = new Date().getTime();
while(new Date().getTime < stop + 15000) {
;
}
}
sleep();
console.log("done");
...would call the sleep(), block the server for the duration of the while loop (15secs) and just THEN print "done" to the console. As far as I understand, this is because Node.js is giving JavaScript only access to the main thread, and therefore this kidn of thing would halt further execution.
So I understand the solution to this is to use callbacks:
function sleep(callback) {
var stop = new Date().getTime();
while(new Date().getTime() < stop + 15000) {
;
}
callback();
}
sleep(function() {
console.log("done sleeping");
});
console.log("DONE");
So I thought this would print 'DONE' and after 15 secs. 'done sleeping', since the sleep() function gets called and is handed a pointer to a callback function. While this function is working (the while loop), the last line would be executed (print 'done'). After 15 seconds, when the sleep() function finishes, it calls the given callback function, which then prints 'done sleeping'.
Apparently I understood something wrong here, because both of the above ways block. Can anybody clarify please?
Thanks in advance,
Slagjoeyoco
Javascript and node.js are single threaded, which means a simple while blocks; no requests/events can be processed until the while block is done. Callbacks don't magically solve this problem, they just help pass custom code to a function. Instead, iterate using process.nextTick, which will give you esentially the same results but leaves space for requests and events to be processed as well, ie, it doesn't block:
function doSleep(callback) {
var stop = new Date().getTime();
process.nextTick(function() {
if(new Date().getTime() < stop + 15000) {
//Done, run callback
if(typeof callback == "function") {
callback();
}
} else {
//Not done, keep looping
process.nextTick(arguments.callee);
}
});
}
doSleep(function() {
console.log("done sleeping");
console.log("DONE");
});
You are calling sleep right away, and the new sleep function blocks. It keeps iterating until the condition is met. You should use setTimeout() to avoid blocking:
setTimeout(function () {
console.log('done sleeping');
}, 15000);
Callbacks aren't the same thing as asynchronicity, they're just helpful when you want to get a... callback... from an asynchronous operation. In your case, the method still executes synchronously; Node doesn't just magically detect that there's a callback and long-running operation, and make it return ahead of time.
The real solution is to use setTimeout instead of a busy loop on another thread.
As already mentioned, asynchronous execution should be achieved by setTimeout() rather than while, because while will freeze in one "execution frame".
Also it seems you have syntax error in your example.
This one works fine: http://jsfiddle.net/6TP76/
I trying to wrap my head around setTimeout, but I can't get it to work properly.
I have set up an example here: http://jsfiddle.net/timkl/Fca2n/
I want a text to countdown after an anchor is clicked - but my setTimeout seems to fire at the same time, even though I've set the delay to 1 sec.
This is my HTML:
Click me!
<span id="target"></span>
This is my JS:
$(document).ready(function() {
function foo(){
writeNumber = $("#target");
setTimeout(writeNumber.html("1"),1000);
setTimeout(writeNumber.html("2"),1000);
setTimeout(writeNumber.html("3"),1000);
};
$('a').click(function() {
foo();
});
});
setTimeout takes a function as an argument. You're executing the function and passing the result into setTimeout (so the function is executed straight away). You can use anonymous functions, for example:
setTimeout(function() {
writeNumber.html("1");
}, 1000);
Note that the same is true of setInterval.
You need to wrap your statements in anonymous functions and also stagger your timings -
setTimeout(function(){writeNumber.html("1")},1000);
setTimeout(function(){writeNumber.html("2")},2000);
setTimeout(function(){writeNumber.html("3")},3000);
If you set everything to 1000 the steps will pretty much run simultaneously as the setTimeout function will run the task 1 second after you called the function not 1 second after the previous call to the setTimeout function finished.
Demo - http://jsfiddle.net/JSe3H/1/
You need to use a function reference to be invoked later when the timer expires. Wrap each statement in an anonymous function so that it isn't executed immediately, but rather when the timer expires.
setTimeout(function() { writeNumber.html("1"); },1000);
Also, you want to use a different delay value for each one so that the timers don't expire at the same time. See an updated fiddle at http://jsfiddle.net/RqCqM/
You need tot use a functions to be called after the timeout is passed; you can use anonymous function too, then your function foo will look like this:
function foo(){
writeNumber = $("#target");
setTimeout(function() { writeNumber.html("1"); },1000);
setTimeout(function() { writeNumber.html("2"); },1000);
setTimeout(function() { writeNumber.html("3"); },1000);
};
There is a provision to pass arguments to the function. In your case, you can do it by
setTimeout(writeNumber.html,1000,1);
setTimeout(writeNumber.html,1000,2);
setTimeout(writeNumber.html,1000,3);
third argument to setTimeout function will be pass to writeNumber.html function
Just use setInterval(). Here's what I came up with. Here's your new javascript:
function foo(){
writeNumber = $("#target");
number = 0;
writeNumber.html(number);
setInterval(function(){
number = number+1;
writeNumber.html(number);
},1000);
};
$('a').click(function() {
foo();
});
I landed on this question. It has been answered adequately, and I think using setInterval as #Purag suggested is probably the best approach to get the desired functional behaviour. However the initial code example did not take JavaScript's asynchroneous behaviour into account. This is an often occurring error, which I've made myself on more than occasion :).
So as a side note I wanted to give another possible solution for this which mimics the initial attempt, but this time DOES consider Javascript's Asynchronousity:
setTimeout(function() {
writeNumber.html("1");
setTimeout(function() {
writeNumber.html("1");
setTimeout(function() {
writeNumber.html("1");
}, 1000);
}, 1000);
}, 1000);
Now ofcourse this is clearly terrible code!
I have given a working JSFiddle of it in my own SO question. This code exemplifies the so-called pyramid of doom. And this can be mitigated by using JavaScript promises, as shown in the answers to my question. It takes some work to write a version of WriteNumber() that uses Promises, but then the code can be rewritten to somehting like:
writeNumAsync(0)
.then(writeNumAsync)
.then(writeNumAsync)
.then(writeNumAsync);