I created this sample web page to create a delay by running the same blocking call multiple times. The issue here is, It's taking some time javascript to count to 50,000 (around 4 seconds) at least it looks like so in the javascript console but the alert call is running before chrome finish counting to 50,000. Why is that?
<html lang="en">
<head>
<title>Document</title>
</head>
<h1>Hello World</h1>
<body>
<script>
for(let i = 0; i < 50000; i++) {
console.log("Loaded")
}
alert("loaded")
console.log('WEB PAGE: Hello World')
</script>
</body>
</html>
Nothing is wrong what your seeing is the delay the browser creates.
Drop the logging in the for loop and use it just to loop that many times will make everything faster.
Note: your not really not describing the point. If the point is to wait for 50,000 what does that mean? is it seconds or less? Is this just a test for some slow api call? Or simply to write out 1-50,000? if you could explain more the answer may be much easier to provide.
see my comment for this add:
var count = 0;
for(let i = 0; i < 50000; i++) {
count++;
}
console.log(count);
console.log will queue the data and write it when the process ends, it doesn't write strings synchronously and because you queued a lot of data, the browser finished counting and showed the alert before the console data was shown.
Related
I'm working on a Javascript Music App that includes a Sequencer. For those who are not familiar, MIDI sequencers work pretty much like this: There is something called PPQ: pulses per quarter note. Each pulse is called "Tick". It depicts how may "subdivisions" there are per quarter note, like resolution. So Sequencers "play" the Events that are in the tracks one Tick at a time: Play Tick1, wait Tick Duration, Play tick2, Tick Duration, and so on.
Now, let's say we have a BPM (Beats per Min) of 120 with PPQ=96 (standard). That means that each Quarter Note Duration is 500ms, and each Tick Duration is 5.20833ms.
What Timer Alternatives we have in Javascript?
1) We have the old setTimeOut. It has several problems: the min. wait time is 4ms. (https://developer.mozilla.org/en-US/docs/Web/API/WindowOrWorkerGlobalScope/setTimeout#Minimum_delay_and_timeout_nesting)
It is also subject to JITTER/time Variations. It is not precise and it is demanding, as call backs are stacked in the even loop.
2) There is an alternative to setTimeOut/setInterval which involves using requestAnimationFrame(). It is VERY precise and CPU efficient. However, the minimum time it can be set is around 16.7ms (the duration of a Frame in a typical 60FPS monitor)
Is there any other Alternative? To to precisely schedule an event every 2-5ms?
Note: the function done in side the loop, playEventsAtTick() is NOT demanding at all, so it would never take more time to execute than Tick Duration.
Thanks!
Danny Bullo
To maintain any sanity in doing this kind of thing, you're going to want to do the audio processing on a devoted thread. Better yet, use the Web Audio API and let people who have been thinking about these problems for a long time do the hard work of sample-accuracy.
Also check out Web MIDI (chrome only).
Thanks nvioli. I'm aware of Web Audio API. However, I don't think that can help here.
I'm not triggering AUDIO directly: I have MIDI events (or let's say just "EVENTS") stored in the TRACKS. And those events happen at any TICK. So the Sequencer needs to loop every Tick Duration to scan what to play at that particular tick.
Regards,
Danny Bullo
In a separate thread, such as a web worker, you can create an endless loop. In this loop, all you need to do is calculate the time between beats. After the time is valid, you can then send a message to the main process, to do some visuals, play a sound or what ever you would like to do.
Here is a Working example
class MyWorker {
constructor() {
// Keeps the loop running
this.run = true
// Beats per minute
this.bpm = 120
// Time last beat was called
this.lastLoopTime = this.milliseconds
}
get milliseconds() {
return new Date().getTime()
}
start() {
while (this.run) {
// Get the current time
let now = this.milliseconds
// Get the elapsed time between now and the last beat
let updateLength = now - this.lastLoopTime
// If not enough time has passed restart from the beginning of the loop
if (updateLength < (1000 * 60) / this.bpm) continue;
// Enough time has passed update the last time
this.lastLoopTime = now
// Do any processing that you would like here
// Send a message back to the main thread
postMessage({ msg: 'beat', time: now })
}
}
}
new MyWorker().start()
Next we can create the index page, which will run the worker, and flash a square everytime a message comes back from the worker.
<!DOCTYPE html>
<html lang="en">
<head>
<script>
// Start the worker
var myWorker = new Worker('worker.js')
// Listen for messages from the worker
myWorker.onmessage = function (e) {
var msg = e.data
switch (msg.msg) {
// If the message is a `beat` message, flash the square
case 'beat':
let div = document.querySelector('div')
div.classList.add('red')
setTimeout(() => div.classList.remove('red'), 100)
break;
}
}
</script>
<style>
div { width: 100px; height: 100px; border: solid 1px; }
.red { background: red; }
</style>
</head>
<body>
<div></div>
</body>
</html>
Get Off My Lawn: The approach you suggested does not completely work. Let's say I add a method to the web worker to STOP the Sequencer:
stop() {
this.run = false;
}
The problem is that the method myWorker.onmessage = function (e) {...} never get's triggered. I suspect it is because the Web Worker Thread is "TOO BUSY" with the endless loop. any way to solve that?
Also, while playing, it works.....but the CPU goes up considerably..... The only possible Solution would be a Sleep() method, but Real SLEEP that does not exist in Javascript...
Thanks
In this article: https://www.html5rocks.com/en/tutorials/speed/script-loading/
They are saying:
Scripts that are dynamically created and added to the document are async by default, they don’t block rendering
But the "execution of javascript" is always blocking rendering, so how can they say they don't block rendering?
I can make it more clear with this example:
SCRIPT.JS
// Synchronous delay of 5 seconds
var timeWhile = new Date().getTime();
while( new Date().getTime() - timeWhile < 5000 );
INDEX.HTML
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title>Test</title>
<script>
['script.js'].forEach( function( src ) {
var script = document.createElement( 'script' );
script.src = src;
script.async = true;
document.head.appendChild(script);
});
// Some other javascript execution, let's say execution delay of 2 seconds.
var timeWhile = new Date().getTime();
while( new Date().getTime() - timeWhile < 2000 );
</script>
</head>
<body>
Some HTML line and this is above the fold
</body>
</html>
I tested it in Chrome and Firefox and both browsers are showing: "Some HTML line and this is above the fold" after 7 seconds and not earlier. So the execution of script.js is blocking rendering, because otherwise the browser would show something after 2 seconds.
Notice: You can also do the test without the delay of 2 seconds, but then you can get different results when repeating the the test. With the delay of 2 seconds, i am giving the browser some extra time, so i am sure script.js has been downloaded, before finishing rendering. Anyway you can get the same results without that delay, but it's only to make it more clear in this post.
What is happening here:
- The browser first will create the element (async script tag with external file script.js)
- Then it starts downloading script.js in parallel, so the everything goes further while downloading script.js
- Then the browser is executing the javascript delay of 2 seconds. Meanwhile script.js has been downloaded.
- Maybe "Some HTML line and this is above the fold" is already in the DOM, but that's not necessary. Anyway it has not been rendered yet, because there is nothing to see yet on the screen.
- Script.js has been downloaded, so it starts to execute the javascript. The execution of Javascript will always block rendering, so now "rendering" has to wait for 5 seconds.
So when scripts are dynamically created, script.js will be downloaded in parallel, but that does not necessarily mean the script is not blocking rendering anymore. They could say the (down)load of script.js is not blocking rendering, but they don't say it like that.
Then how they can say something like that? I don't see it only here, but also in other official Google documentation.
Now people can read it and make something like my example (i only made the "execution time" more bigger with delays, to make it more clear). Then people can think: Nice! The javascript is not blocking rendering, but actually it is blocking, so they could better make other choises in terms of page speed.
I wrote this code which is supposed to say "hi" when I click the "hello" button:
<!DOCTYPE html>
<html>
<head>
<script>
var someLargeNumber = 5000000000;
function hello() {
document.getElementById('hi').innerHTML = "hi";
for(var i = 0; i < someLargeNumber; i++) {}
}
</script>
</head>
<body>
<p id="hi"></p>
<input type="button" value="hello" onclick="hello();">
</body>
</html>
It does say hi, but only after the for loop is finished. Why does this happen and how do I fix this?
Thanks
Why does this happen...
Because browsers run JavaScript on the main UI thread they use for updating the page, for a variety of reasons. So although you've shown the "hi" text, it doesn't get rendered until the JavaScript code running in response to the event completes.
...and how do I fix this?
Yield back to the browser after adding the text, before doing whatever it is that you're simulating with that loop. setTimeout with a delay of 0 is suitable for many cases:
var someLargeNumber = 5000000000;
function hello() {
document.getElementById('hi').innerHTML = "hi";
setTimeout(function() {
for(var i = 0; i < someLargeNumber; i++) {}
}, 0);
}
The JavaScript engine works basically in a loop with a task queue (the spec calls them "jobs"). It picks up a job from the queue, runs it to completion, and then looks for the next job. Browsers (usually) update the UI when the engine is between jobs. When an event occurs, a job is queued to call the event handler. The above just moves the loop into a new job it queues via setTimeout, so the browser has a chance after the event job and before the setTimeout job to update the UI.
As already answered browser has single UI thread.
Another option is to use Web Worker (provided you are not doing any DOM manipulations in the worker thread), which allows to run operations in an another thread.
Add another js file (say worker.js)
var someLargeNumber = 5000000000;
onmessage = function(e) {
console.log('Message received from main script');
for(var i = 0; i < someLargeNumber; i++) {}
console.log('Posting message back to main script');
postMessage('done');
}
Back in you main file
<head>
<script>
var myWorker = new Worker("worker.js");
function hello() {
document.getElementById('hi').innerHTML = "hi";
myWorker.postMessage('test');
console.log('Message posted to worker');
}
myWorker.onmessage = function(e) {
result.textContent = e.data;
console.log('Worker thread is complete');
}
</script>
I'm writing Javascript which is counting up to a certain number for a project. The number could be around 100,000 and It will take roughly 10-15 seconds to complete processing.
I want the script to run as soon as the user calls the page and when the script completes it does a redirect.
Is it possible to pause for even 10ms to update the DOM while it is running to give feedback such as "Still working"?
I would like to avoid the use of jQuery and web-workers are not an option in this situation.
I realise this isn't a real world application!
EDIT: Added some of the code as a sample:
In the head
function myCounter (target) {
var t = target;
var count = 0;
while (t != count){
if (t == count) {
window.location.replace("http://example.com"); // redirect
}
count++;
}
}
In the body
<script>myCounter(100000);</script>
In most browsers JavaScript and the UI run in the same thread. You can give the thread back to the browser by using setTimeout (or setInterval).
var myNumber = 0;
updateNumber();
function updateNumber(){
// do heavy work for great good, ideally divided into smaller chunks
document.getElementById('updateDiv').innerHTML = 'still working, up to ' + myNumber;
if(myNumber < limit) {
setTimeout(updateNumber, 20);
}
}
For a lot more details on the general process, this answer is worth a read: https://stackoverflow.com/a/4575011/194940
Is there anyway to do this? I want to create a JavaScript application that can "resume" application from the last checkpoint e.g.
//code.js
var abc = 13;
checkpoint("myLocalStorage");
alert(abc);
the checkpoint function will store every info about the execution such that in the future execution can be resumed right where it was left of as such:
//resume.js
resume("myLocalStorage");
This would be very helpful for executing long script / script with huge loops - I'm not talking about executing some tiny script that preloads images or do some funny animations. I'm talking about using JavaScript as a real number crunching tool where execution can take a long time and demands huge computing power. In these context you can see how useful execution checkpointing could be!
I suppose such thing doesn't exist for JavaScript yet, but if anyone ever comes close to something that remotely resembles it I would still be very grateful.
In order to make something that "suspend-able" in Javascript, you need to formulate things a little differently than you would in normal program.
Step 1
Decide how much of the problem you are able to do in one pass, for lack of a better word.
Step 2
Store the state in some kind of object. You have no intermediate values, just exactly what is needed to make the next pass
Step 3
Write the code so it can run with a window.setTimeout() function. This makes testing much easier than reloading the page.
In this case, I have a program that converts my whole name to lower case, one step at a time. The only data I need to save is my name, and an index of where along the calculations I am.
Example 1: Uses setTimeout()
<html>
<head>
<title>Test Thingy</title>
</head>
<body>
<script>
var data = {
name: ["Jeremy", "J", "Starcher"],
idx: 0
}
function doPass() {
// If at the end of the list
if (data.idx >= data.name.length) {
alert("All iterations done:" + data.name.join(" "));
return;
}
// Do our calculation here
var s = data.name[data.idx];
s = s.toLowerCase();
data.name[data.idx] = s;
data.idx++;
window.setTimeout(doPass);
}
doPass();
</script>
</body>
</html>
Example 2: Uses localStorage. Hit 'reload' 4 times to test
<html>
<head>
<title>Test Thingy</title>
</head>
<body>
<script>
var data;
data = localStorage.getItem("data");
if (data) {
data = JSON.parse(data);
} else {
data = {
name: ["Jeremy", "J", "Starcher"],
idx: 0
}
}
function doPass() {
// If at the end of the list
if (data.idx >= data.name.length) {
alert("All iterations done:" + data.name.join(" "));
return;
}
// Do our calculation here
var s = data.name[data.idx];
alert(s);
s = s.toLowerCase();
data.name[data.idx] = s;
data.idx++;
localStorage.setItem("data", JSON.stringify(data));
}
doPass();
</script>
</body>
</html>
Javascript is not designed for "[storage on] real number crunching tool where execution can take a long time and demands huge computing power". Here's the best you'll get: http://www.w3schools.com/html/html5_webstorage.asp
The latest browsers support yield. We can look into it.
https://developer.mozilla.org/en-US/docs/JavaScript/New_in_JavaScript/1.7