Related
EDIT: per Yogi's comment (see "setTimeout" and "throttling" in https://developer.mozilla.org/en-US/docs/Web/API/setTimeout ), I've tried adding an AudioContext to prevent the slowdown.
document.addEventListener('click', ev => {
let audCtxt = new AudioContext({});
});
(AudioContext needs user interaction, hence the event listener.)
But, no luck.
Other ideas I'm noting here to follow up are
maybe a while loop, checking Date.now() for multiples of 10ms -- but I think that would crash the page
maybe using requestAnimationFrame?
Original post:
I have a setTimeout firing every 0.01 seconds that's acting as a master clock for my web app.
The app plays synchronized sounds that respond to user interaction, hence the need for a master clock. Simplified:
let counter = 0;
setTimeout(() => {
counter++;
console.log(counter);
}, 10);
When on a mobile device, the setTimeout slows down (about 2-4x) when the screen is locked/off. (Tested on Android, not iOS).
This can be verified by logging, like the above, or by generating a sound when the counter is multiple of 100.
How can I prevent this?
Should I be taking a different approach to a "master clock" that synchronizes triggering audio samples while still allowing the audio to respond in real time to user interaction?
setTimeout is not reliable as other things, such as promises, have higher execution priority. One possible workaround is to create a custom timer using promises. Here is an example:
var customDelay = new Promise(function (resolve) {
var delay = 10; // milliseconds
var before = Date.now();
while (Date.now() < before + delay) { };
resolve();
});
customDelay.then(function () {
//Timer triggered
});
Update 1:
Given that you want a 10ms update frequency, running the above code on the main thread ends up locking up the UI due to the while loop. With that in mind, offloading that while loop into a web worker would resolve this. Here is some code:
<html>
<head>
<title></title>
</head>
<body>
<script id="FastTimer" type="javascript/worker">
onmessage = function (event) {
var delay = 10; // milliseconds
var before = Date.now();
while (Date.now() < before + delay) { };
postMessage({data: []});
};
</script>
<script>
var worker;
window.onload = function() {
var blob = new Blob([document.querySelector("#FastTimer").textContent]);
blobURL = window.URL.createObjectURL(blob);
worker = new Worker(blobURL);
worker.addEventListener("message", receivedWorkerMessage);
worker.onerror = workerError;
//Start the worker.
worker.postMessage({});
}
var counter = 0;
function receivedWorkerMessage(event) {
worker.postMessage({});
timerTiggered();
}
function timerTiggered() {
counter++;
console.log(counter);
}
function workerError(error) {
alert(error.message);
}
function stopWorker() {
worker.terminate();
worker = null;
}
</script>
</body>
</html>
The main issue with the above is that I suspect there would be some sort of time cost going back and forth between the worker (maybe a couple ms, hard to say).
As mentioned, normally requestAnimationFrame is used for animations in web apps. However, this would likely not fire when the screen is locked. But if you want to try, here is a sample:
<html>
<head>
<title></title>
</head>
<body>
<script>
var counter = 0;
var minTimeSpan = 10;
var lastTime = performance.now();
function animate() {
let t = performance.now();
if (t - lastTime >= minTimeSpan) {
timerTiggered();
}
requestAnimationFrame(animate);
}
function timerTiggered() {
counter++;
console.log(counter);
}
animate();
</script>
</body>
</html>
This can usually be solved by using a Web Worker for running the timer. I created a library which looks like setTimeout() but uses a Web Worker internally.
https://github.com/chrisguttandin/worker-timers
But there was a bug a while ago in some browser (I forgot which one it was) which caused this to not work any longer. Therefore I built the same abstraction on top of a running AudioContext.
https://github.com/chrisguttandin/audio-context-timers
But as you already said this only works if the page is already allowed to run an AudioContext by starting one in response to a click handler.
I'm using angularjs as front-end framework. I get a data set via a rest get call.
That contains coordinates in an array. What needs to be done is that array with coordinates should be iterated and should be shown in a map.
Once the user hits play in the map those coordinates should be displayed one ofter another having at least 1 second of interval.
And when user hits play button, the button it self converts to a pause button it should do what the name intend to do. pause the process. I couldn't achive this kind of behavior using angularjs. Following is the closest that I could come.
var getReplayData = function () {
return $http.get('http://localhost:4000/replay/asiri/vin/' + from + '/' + to);
};
$scope.play = function () {
from = rangeProperties.getFrom() * 1000;
to = rangeProperties.getTo() * 1000;
getReplayData().success(function (data) {
console.log(data);
var waitingTime = 0;
var gap = 0;
for (var i = 0; i < data.length; i++) {
(function (i) {
var element = data[i];
var coordinates = new Object();
coordinates.latitude = element.LATITUDE;
coordinates.longitude = element.LONGITUDE;
setTimeout(function () {
broadcastData(coordinates);
}, waitingTime);
if (i + 1 < data.length) {
gap = data[i + 1].TIMESTAMP - element.TIMESTAMP;
console.log(gap);
} else {
gap = 0;
}
waitingTime = waitingTime + gap;
})(i);
}
});
$scope.play refers to the play action of the button. I can't figure out how to pause this process. Seems like I might have to keep the references to all timeouts and cancel them. Any idea how to implement this kinda of scenario? I don't exactly need a code segment just an idea how to approach to solve this kinda of problem would be really nice.
Seems like I might have to keep the references to all timeouts and cancel them.
That would be a good first step.
I don't exactly need a code segment just an idea how to approach to solve this kinda of problem would be really nice
I would do the following:
cache the response data as a variable, if possible.
separate the play and pause functionality, tying them into a cancelable callback that you get from using the $interval service.
during playback, treat your coordinates data as a FIFO (queue), dequeuing the item which gets passed to the $interval's promise
during pause, you simply cancel the $interval promise
playing again doesn't require any fancy work on the queue as you'll resume the same logic on the first item in the queue when you resume.
I have a PhantomJS script that I'm trying to use in order to essentially generate a video of a particular website.
var page = require('webpage').create();
page.viewportSize = {
width: 1280,
height: 720
};
page.open('http://my-awesome-site.whatever', function() {
var fps = 30;
var seconds = 10;
var elapsed = 0;
var current = 0;
takeScreenShot();
function takeScreenShot() {
if (elapsed < seconds * 1000) {
page.render('screenshot_' + (current++) + '.png');
elapsed += 1000 / fps;
setTimeout(takeScreenShot, 1000 / fps);
}
else {
phantom.exit();
}
}
});
The above script will attempt to take 30 screenshots per second for 10 seconds, which I then combine into an mp4 using ffmpeg.
The problem is that the page.render() function is not instantaneous, and does not halt the scripts running on the page. So when I'm pointing at a page using jQuery animations, which I believe rely on setTimeout, those timeouts continue to run while each screenshot is processed. As a result, the outputted video appears greatly sped up.
Is there a way through PhantomJS to pause script execution? What I'm hoping for is to do something like:
page.pause();
page.render('screenshot_' + (current++) + '.png');
page.resume();
But sadly, I don't see anything like that in their api docs.
You could set page.settings.javascriptEnabled=false if the page looks don't depend on JS too much.
Be aware that you need to define the settings before the first call as they are evaluated only once eg :
var page = require('webpage').create();
page.settings = {
javascriptEnabled=false
}
See : http://phantomjs.org/api/webpage/property/settings.html
If it doesn't fit your need, you need to dig into the code of the page to find a way to stop the animations (use your browser's console). Once you know what you need to do, just evaluate the command in Phantom.
page.open('http://my-awesome-site.whatever', function() {
var stopScripts = page.evaluate(function() {
// do whatever you need to stop execution
return true;
});
// ... take your screenshots
});
I have a button which runs a long running function when it's clicked. Now, while the function is running, I want to change the button text, but I'm having problems in some browsers like Firefox, IE.
html:
<button id="mybutt" class="buttonEnabled" onclick="longrunningfunction();"><span id="myspan">do some work</span></button>
javascript:
function longrunningfunction() {
document.getElementById("myspan").innerHTML = "doing some work";
document.getElementById("mybutt").disabled = true;
document.getElementById("mybutt").className = "buttonDisabled";
//long running task here
document.getElementById("myspan").innerHTML = "done";
}
Now this has problems in firefox and IE, ( in chrome it works ok )
So I thought to put it into a settimeout:
function longrunningfunction() {
document.getElementById("myspan").innerHTML = "doing some work";
document.getElementById("mybutt").disabled = true;
document.getElementById("mybutt").className = "buttonDisabled";
setTimeout(function() {
//long running task here
document.getElementById("myspan").innerHTML = "done";
}, 0);
}
but this doesn't work either for firefox! the button gets disabled, changes colour ( due to the application of the new css ) but the text does not change.
I have to set the time to 50ms instead of just 0ms, in order to make it work ( change the button text ). Now I find this stupid at least. I can understand if it would work with just a 0ms delay, but what would happen in a slower computer? maybe firefox would need 100ms there in the settimeout? it sounds rather stupid. I tried many times, 1ms, 10ms, 20ms...no it won't refresh it. only with 50ms.
So I followed the advice in this topic:
Forcing a DOM refresh in Internet explorer after javascript dom manipulation
so I tried:
function longrunningfunction() {
document.getElementById("myspan").innerHTML = "doing some work";
var a = document.getElementById("mybutt").offsetTop; //force refresh
//long running task here
document.getElementById("myspan").innerHTML = "done";
}
but it doesn't work ( FIREFOX 21). Then i tried:
function longrunningfunction() {
document.getElementById("myspan").innerHTML = "doing some work";
document.getElementById("mybutt").disabled = true;
document.getElementById("mybutt").className = "buttonDisabled";
var a = document.getElementById("mybutt").offsetTop; //force refresh
var b = document.getElementById("myspan").offsetTop; //force refresh
var c = document.getElementById("mybutt").clientHeight; //force refresh
var d = document.getElementById("myspan").clientHeight; //force refresh
setTimeout(function() {
//long running task here
document.getElementById("myspan").innerHTML = "done";
}, 0);
}
I even tried clientHeight instead of offsetTop but nothing. the DOM does not get refreshed.
Can someone offer a reliable solution preferrably non-hacky ?
thanks in advance!
as suggested here i also tried
$('#parentOfElementToBeRedrawn').hide().show();
to no avail
Force DOM redraw/refresh on Chrome/Mac
TL;DR:
looking for a RELIABLE cross-browser method to have a forced DOM refresh WITHOUT the use of setTimeout (preferred solution due to different time intervals needed depending on the type of long running code, browser, computer speed and setTimeout requires anywhere from 50 to 100ms depending on situation)
jsfiddle: http://jsfiddle.net/WsmUh/5/
Webpages are updated based on a single thread controller, and half the browsers don't update the DOM or styling until your JS execution halts, giving computational control back to the browser. That means if you set some element.style.[...] = ... it won't kick in until your code finishes running (either completely, or because the browser sees you're doing something that lets it intercept processing for a few ms).
You have two problems: 1) your button has a <span> in it. Remove that, just set .innerHTML on the button itself. But this isn't the real problem of course. 2) you're running very long operations, and you should think very hard about why, and after answering the why, how:
If you're running a long computational job, cut it up into timeout callbacks (or, in 2019, await/async - see note at the end of this anser). Your examples don't show what your "long job" actually is (a spin loop doesn't count) but you have several options depending on the browsers you take, with one GIANT booknote: don't run long jobs in JavaScript, period. JavaScript is a single threaded environment by specification, so any operation you want to do should be able to complete in milliseconds. If it can't, you're literally doing something wrong.
If you need to calculate difficult things, offload it to the server with an AJAX operation (universal across browsers, often giving you a) faster processing for that operation and b) a good 30 seconds of time that you can asynchronously not-wait for the result to be returned) or use a webworker background thread (very much NOT universal).
If your calculation takes long but not absurdly so, refactor your code so that you perform parts, with timeout breathing space:
function doLongCalculation(callbackFunction) {
var partialResult = {};
// part of the work, filling partialResult
setTimeout(function(){ doSecondBit(partialResult, callbackFunction); }, 10);
}
function doSecondBit(partialResult, callbackFunction) {
// more 'part of the work', filling partialResult
setTimeout(function(){ finishUp(partialResult, callbackFunction); }, 10);
}
function finishUp(partialResult, callbackFunction) {
var result;
// do last bits, forming final result
callbackFunction(result);
}
A long calculation can almost always be refactored into several steps, either because you're performing several steps, or because you're running the same computation a million times, and can cut it up into batches. If you have (exaggerated) this:
var resuls = [];
for(var i=0; i<1000000; i++) {
// computation is performed here
if(...) results.push(...);
}
then you can trivially cut this up into a timeout-relaxed function with a callback
function runBatch(start, end, terminal, results, callback) {
var i;
for(var i=start; i<end; i++) {
// computation is performed here
if(...) results.push(...); }
if(i>=terminal) {
callback(results);
} else {
var inc = end-start;
setTimeout(function() {
runBatch(start+inc, end+inc, terminal, results, callback);
},10);
}
}
function dealWithResults(results) {
...
}
function doLongComputation() {
runBatch(0,1000,1000000,[],dealWithResults);
}
TL;DR: don't run long computations, but if you have to, make the server do the work for you and just use an asynchronous AJAX call. The server can do the work faster, and your page won't block.
The JS examples of how to deal with long computations in JS at the client are only here to explain how you might deal with this problem if you don't have the option to do AJAX calls, which 99.99% of the time will not be the case.
edit
also note that your bounty description is a classic case of The XY problem
2019 edit
In modern JS the await/async concept vastly improves upon timeout callbacks, so use those instead. Any await lets the browser know that it can safely run scheduled updates, so you write your code in a "structured as if it's synchronous" way, but you mark your functions as async, and then you await their output them whenever you call them:
async doLongCalculation() {
let firstbit = await doFirstBit();
let secondbit = await doSecondBit(firstbit);
let result = await finishUp(secondbit);
return result;
}
async doFirstBit() {
//...
}
async doSecondBit...
...
SOLVED IT!! No setTimeout()!!!
Tested in Chrome 27.0.1453, Firefox 21.0, Internet 9.0.8112
$("#btn").on("mousedown",function(){
$('#btn').html('working');}).on('mouseup', longFunc);
function longFunc(){
//Do your long running work here...
for (i = 1; i<1003332300; i++) {}
//And on finish....
$('#btn').html('done');
}
DEMO HERE!
As of 2019 one uses double requesAnimationFrame to skip a frame instead of creating a race condition using setTimeout.
function doRun() {
document.getElementById('app').innerHTML = 'Processing JS...';
requestAnimationFrame(() =>
requestAnimationFrame(function(){
//blocks render
confirm('Heavy load done')
document.getElementById('app').innerHTML = 'Processing JS... done';
}))
}
doRun()
<div id="app"></div>
As an usage example think of calculating pi using Monte Carlo in an endless loop:
using for loop to mock while(true) - as this breaks the page
function* piMonteCarlo(r = 5, yield_cycle = 10000){
let total = 0, hits = 0, x=0, y=0, rsqrd = Math.pow(r, 2);
while(true){
total++;
if(total === Number.MAX_SAFE_INTEGER){
break;
}
x = Math.random() * r * 2 - r;
y = Math.random() * r * 2 - r;
(Math.pow(x,2) + Math.pow(y,2) < rsqrd) && hits++;
if(total % yield_cycle === 0){
yield 4 * hits / total
}
}
}
let pi_gen = piMonteCarlo(5, 1000), pi = 3;
for(let i = 0; i < 1000; i++){
// mocks
// while(true){
// basically last value will be rendered only
pi = pi_gen.next().value
console.log(pi)
document.getElementById('app').innerHTML = "PI: " + pi
}
<div id="app"></div>
And now think about using requestAnimationFrame for updates in between ;)
function* piMonteCarlo(r = 5, yield_cycle = 10000){
let total = 0, hits = 0, x=0, y=0, rsqrd = Math.pow(r, 2);
while(true){
total++;
if(total === Number.MAX_SAFE_INTEGER){
break;
}
x = Math.random() * r * 2 - r;
y = Math.random() * r * 2 - r;
(Math.pow(x,2) + Math.pow(y,2) < rsqrd) && hits++;
if(total % yield_cycle === 0){
yield 4 * hits / total
}
}
}
let pi_gen = piMonteCarlo(5, 1000), pi = 3;
function rAFLoop(calculate){
return new Promise(resolve => {
requestAnimationFrame( () => {
requestAnimationFrame(() => {
typeof calculate === "function" && calculate()
resolve()
})
})
})
}
let stopped = false
async function piDOM(){
while(stopped==false){
await rAFLoop(() => {
pi = pi_gen.next().value
console.log(pi)
document.getElementById('app').innerHTML = "PI: " + pi
})
}
}
function stop(){
stopped = true;
}
function start(){
if(stopped){
stopped = false
piDOM()
}
}
piDOM()
<div id="app"></div>
<button onclick="stop()">Stop</button>
<button onclick="start()">start</button>
As described in the "Script taking too long and heavy jobs" section of Events and timing in-depth (an interesting reading, by the way):
[...] split the job into parts which get scheduled after each other. [...] Then there is a “free time” for the browser to respond between parts. It is can render and react on other events. Both the visitor and the browser are happy.
I am sure that there are many times in which a task cannot be splitted into smaller tasks, or fragments. But I am sure that there will be many other times in which this is possible too! :-)
Some refactoring is needed in the example provided. You could create a function to do a piece of the work you have to do. It could begin like this:
function doHeavyWork(start) {
var total = 1000000000;
var fragment = 1000000;
var end = start + fragment;
// Do heavy work
for (var i = start; i < end; i++) {
//
}
Once the work is finished, function should determine if next work piece must be done, or if execution has finished:
if (end == total) {
// If we reached the end, stop and change status
document.getElementById("btn").innerHTML = "done!";
} else {
// Otherwise, process next fragment
setTimeout(function() {
doHeavyWork(end);
}, 0);
}
}
Your main dowork() function would be like this:
function dowork() {
// Set "working" status
document.getElementById("btn").innerHTML = "working";
// Start heavy process
doHeavyWork(0);
}
Full working code at http://jsfiddle.net/WsmUh/19/ (seems to behave gently on Firefox).
If you don't want to use setTimeout then you are left with WebWorker - this will require HTML5 enabled browsers however.
This is one way you can use them -
Define your HTML and an inline script (you don't have to use inline script, you can just as well give an url to an existing separate JS file):
<input id="start" type="button" value="Start" />
<div id="status">Preparing worker...</div>
<script type="javascript/worker">
postMessage('Worker is ready...');
onmessage = function(e) {
if (e.data === 'start') {
//simulate heavy work..
var max = 500000000;
for (var i = 0; i < max; i++) {
if ((i % 100000) === 0) postMessage('Progress: ' + (i / max * 100).toFixed(0) + '%');
}
postMessage('Done!');
}
};
</script>
For the inline script we mark it with type javascript/worker.
In the regular Javascript file -
The function that converts the inline script to a Blob-url that can be passed to a WebWorker. Note that this might note work in IE and you will have to use a regular file:
function getInlineJS() {
var js = document.querySelector('[type="javascript/worker"]').textContent;
var blob = new Blob([js], {
"type": "text\/plain"
});
return URL.createObjectURL(blob);
}
Setup worker:
var ww = new Worker(getInlineJS());
Receive messages (or commands) from the WebWorker:
ww.onmessage = function (e) {
var msg = e.data;
document.getElementById('status').innerHTML = msg;
if (msg === 'Done!') {
alert('Next');
}
};
We kick off with a button-click in this demo:
document.getElementById('start').addEventListener('click', start, false);
function start() {
ww.postMessage('start');
}
Working example here:
http://jsfiddle.net/AbdiasSoftware/Ls4XJ/
As you can see the user-interface is updated (with progress in this example) even if we're using a busy-loop on the worker. This was tested with an Atom based (slow) computer.
If you don't want or can't use WebWorker you have to use setTimeout.
This is because this is the only way (beside from setInterval) that allow you to queue up an event. As you noticed you will need to give it a few milliseconds as this will give the UI engine "room to breeth" so-to-speak. As JS is single-threaded you cannot queue up events other ways (requestAnimationFrame will not work well in this context).
Hope this helps.
Update: I don't think in the long term that you can be sure of avoiding Firefox's aggressive avoidance of DOM updates without using a timeout. If you want to force a redraw / DOM update, there are tricks available, like adjusting the offset of elements, or doing hide() then show(), etc., but there is nothing very pretty available, and after a while when those tricks get abused and slow down user experience, then browsers get updated to ignore those tricks. See this article and the linked articles beside it for some examples: Force DOM redraw/refresh on Chrome/Mac
The other answers look like they have the basic elements needed, but I thought it would be worthwhile to mention that my practice is to wrap all interactive DOM-changing functions in a "dispatch" function which handles the necessary pauses needed to get around the fact that Firefox is extremely aggressive in avoiding DOM updates in order to score well on benchmarks (and to be responsive to users while browsing the internet).
I looked at your JSFiddle and customized a dispatch function the one that many of my programs rely on. I think it is self-explanatory, and you can just paste it into your existing JS Fiddle to see how it works:
$("#btn").on("click", function() { dispatch(this, dowork, 'working', 'done!'); });
function dispatch(me, work, working, done) {
/* work function, working message HTML string, done message HTML string */
/* only designed for a <button></button> element */
var pause = 50, old;
if (!me || me.tagName.toLowerCase() != 'button' || me.innerHTML == working) return;
old = me.innerHTML;
me.innerHTML = working;
setTimeout(function() {
work();
me.innerHTML = done;
setTimeout(function() { me.innerHTML = old; }, 1500);
}, pause);
}
function dowork() {
for (var i = 1; i<1000000000; i++) {
//
}
}
Note: the dispatching function also blocks calls from happening at the same time, because it can seriously confuse users if status updates from multiple clicks are happening together.
Fake an ajax request
function longrunningfunction() {
document.getElementById("myspan").innerHTML = "doing some work";
document.getElementById("mybutt").disabled = true;
document.getElementById("mybutt").className = "buttonDisabled";
$.ajax({
url: "/",
complete: function () {
//long running task here
document.getElementById("myspan").innerHTML = "done";
}
});}
Try this
function longRunningTask(){
// Do the task here
document.getElementById("mybutt").value = "done";
}
function longrunningfunction() {
document.getElementById("mybutt").value = "doing some work";
setTimeout(function() {
longRunningTask();
}, 1);
}
Some browsers don't handle onclick html attribute good. It's better to use that event on js object. Like this:
<button id="mybutt" class="buttonEnabled">
<span id="myspan">do some work</span>
</button>
<script type="text/javascript">
window.onload = function(){
butt = document.getElementById("mybutt");
span = document.getElementById("myspan");
butt.onclick = function () {
span.innerHTML = "doing some work";
butt.disabled = true;
butt.className = "buttonDisabled";
//long running task here
span.innerHTML = "done";
};
};
</script>
I made a fiddle with working example http://jsfiddle.net/BZWbH/2/
Have you tried adding listener to "onmousedown" to change the button text and click event for longrunning function.
Slightly modified your code at jsfiddle and:
$("#btn").on("click", dowork);
function dowork() {
document.getElementById("btn").innerHTML = "working";
setTimeout(function() {
for (var i = 1; i<1000000000; i++) {
//
}
document.getElementById("btn").innerHTML = "done!";
}, 100);
}
Timeout set to more reasonable value 100ms did the trick for me. Try it.
Try adjusting the latency to find the best value.
DOM buffer also exists in default browser on android,
long running javascript only flush DOM buffer once,
use setTimeout(..., 50) to solve it.
I have adapted Estradiaz's double animation frame method for async/await:
async function waitForDisplayUpdate() {
await waitForNextAnimationFrame();
await waitForNextAnimationFrame();
}
function waitForNextAnimationFrame() {
return new Promise((resolve) => {
window.requestAnimationFrame(() => resolve());
});
}
async function main() {
const startTime = performance.now();
for (let i = 1; i <= 5; i++) {
setStatus("Step " + i);
await waitForDisplayUpdate();
wasteCpuTime(1000);
}
const elapsedTime = Math.round(performance.now() - startTime);
setStatus(`Completed in ${elapsedTime} ms`);
}
function wasteCpuTime(ms) {
const startTime = performance.now();
while (performance.now() < startTime + ms) {
if (Math.random() == 0) {
console.log("A very rare event has happened.");
}
}
}
function setStatus(s) {
document.getElementById("status").textContent = s;
}
document.addEventListener("DOMContentLoaded", main);
Status: <span id="status">Start</span>
Is there some way to do multi-threading in JavaScript?
See http://caniuse.com/#search=worker for the most up-to-date support info.
The following was the state of support circa 2009.
The words you want to google for are JavaScript Worker Threads
Apart from from Gears there's nothing available right now, but there's plenty of talk about how to implement this so I guess watch this question as the answer will no doubt change in future.
Here's the relevant documentation for Gears: WorkerPool API
WHATWG has a Draft Recommendation for worker threads: Web Workers
And there's also Mozilla’s DOM Worker Threads
Update: June 2009, current state of browser support for JavaScript threads
Firefox 3.5 has web workers. Some demos of web workers, if you want to see them in action:
Simulated Annealing ("Try it" link)
Space Invaders (link at end of post)
MoonBat JavaScript Benchmark (first link)
The Gears plugin can also be installed in Firefox.
Safari 4, and the WebKit nightlies have worker threads:
JavaScript Ray Tracer
Chrome has Gears baked in, so it can do threads, although it requires a confirmation prompt from the user (and it uses a different API to web workers, although it will work in any browser with the Gears plugin installed):
Google Gears WorkerPool Demo (not a good example as it runs too fast to test in Chrome and Firefox, although IE runs it slow enough to see it blocking interaction)
IE8 and IE9 can only do threads with the Gears plugin installed
Different way to do multi-threading and Asynchronous in JavaScript
Before HTML5 JavaScript only allowed the execution of one thread per page.
There was some hacky way to simulate an asynchronous execution with Yield, setTimeout(), setInterval(), XMLHttpRequest or event handlers (see the end of this post for an example with yield and setTimeout()).
But with HTML5 we can now use Worker Threads to parallelize the execution of functions. Here is an example of use.
Real multi-threading
Multi-threading: JavaScript Worker Threads
HTML5 introduced Web Worker Threads (see: browsers compatibilities)
Note: IE9 and earlier versions do not support it.
These worker threads are JavaScript threads that run in background without affecting the performance of the page. For more information about Web Worker read the documentation or this tutorial.
Here is a simple example with 3 Web Worker threads that count to MAX_VALUE and show the current computed value in our page:
//As a worker normally take another JavaScript file to execute we convert the function in an URL: http://stackoverflow.com/a/16799132/2576706
function getScriptPath(foo){ return window.URL.createObjectURL(new Blob([foo.toString().match(/^\s*function\s*\(\s*\)\s*\{(([\s\S](?!\}$))*[\s\S])/)[1]],{type:'text/javascript'})); }
var MAX_VALUE = 10000;
/*
* Here are the workers
*/
//Worker 1
var worker1 = new Worker(getScriptPath(function(){
self.addEventListener('message', function(e) {
var value = 0;
while(value <= e.data){
self.postMessage(value);
value++;
}
}, false);
}));
//We add a listener to the worker to get the response and show it in the page
worker1.addEventListener('message', function(e) {
document.getElementById("result1").innerHTML = e.data;
}, false);
//Worker 2
var worker2 = new Worker(getScriptPath(function(){
self.addEventListener('message', function(e) {
var value = 0;
while(value <= e.data){
self.postMessage(value);
value++;
}
}, false);
}));
worker2.addEventListener('message', function(e) {
document.getElementById("result2").innerHTML = e.data;
}, false);
//Worker 3
var worker3 = new Worker(getScriptPath(function(){
self.addEventListener('message', function(e) {
var value = 0;
while(value <= e.data){
self.postMessage(value);
value++;
}
}, false);
}));
worker3.addEventListener('message', function(e) {
document.getElementById("result3").innerHTML = e.data;
}, false);
// Start and send data to our worker.
worker1.postMessage(MAX_VALUE);
worker2.postMessage(MAX_VALUE);
worker3.postMessage(MAX_VALUE);
<div id="result1"></div>
<div id="result2"></div>
<div id="result3"></div>
We can see that the three threads are executed in concurrency and print their current value in the page. They don't freeze the page because they are executed in the background with separated threads.
Multi-threading: with multiple iframes
Another way to achieve this is to use multiple iframes, each one will execute a thread. We can give the iframe some parameters by the URL and the iframe can communicate with his parent in order to get the result and print it back (the iframe must be in the same domain).
This example doesn't work in all browsers! iframes usually run in the same thread/process as the main page (but Firefox and Chromium seem to handle it differently).
Since the code snippet does not support multiple HTML files, I will just provide the different codes here:
index.html:
//The 3 iframes containing the code (take the thread id in param)
<iframe id="threadFrame1" src="thread.html?id=1"></iframe>
<iframe id="threadFrame2" src="thread.html?id=2"></iframe>
<iframe id="threadFrame3" src="thread.html?id=3"></iframe>
//Divs that shows the result
<div id="result1"></div>
<div id="result2"></div>
<div id="result3"></div>
<script>
//This function is called by each iframe
function threadResult(threadId, result) {
document.getElementById("result" + threadId).innerHTML = result;
}
</script>
thread.html:
//Get the parameters in the URL: http://stackoverflow.com/a/1099670/2576706
function getQueryParams(paramName) {
var qs = document.location.search.split('+').join(' ');
var params = {}, tokens, re = /[?&]?([^=]+)=([^&]*)/g;
while (tokens = re.exec(qs)) {
params[decodeURIComponent(tokens[1])] = decodeURIComponent(tokens[2]);
}
return params[paramName];
}
//The thread code (get the id from the URL, we can pass other parameters as needed)
var MAX_VALUE = 100000;
(function thread() {
var threadId = getQueryParams('id');
for(var i=0; i<MAX_VALUE; i++){
parent.threadResult(threadId, i);
}
})();
Simulate multi-threading
Single-thread: emulate JavaScript concurrency with setTimeout()
The 'naive' way would be to execute the function setTimeout() one after the other like this:
setTimeout(function(){ /* Some tasks */ }, 0);
setTimeout(function(){ /* Some tasks */ }, 0);
[...]
But this method does not work because each task will be executed one after the other.
We can simulate asynchronous execution by calling the function recursively like this:
var MAX_VALUE = 10000;
function thread1(value, maxValue){
var me = this;
document.getElementById("result1").innerHTML = value;
value++;
//Continue execution
if(value<=maxValue)
setTimeout(function () { me.thread1(value, maxValue); }, 0);
}
function thread2(value, maxValue){
var me = this;
document.getElementById("result2").innerHTML = value;
value++;
if(value<=maxValue)
setTimeout(function () { me.thread2(value, maxValue); }, 0);
}
function thread3(value, maxValue){
var me = this;
document.getElementById("result3").innerHTML = value;
value++;
if(value<=maxValue)
setTimeout(function () { me.thread3(value, maxValue); }, 0);
}
thread1(0, MAX_VALUE);
thread2(0, MAX_VALUE);
thread3(0, MAX_VALUE);
<div id="result1"></div>
<div id="result2"></div>
<div id="result3"></div>
As you can see this second method is very slow and freezes the browser because it uses the main thread to execute the functions.
Single-thread: emulate JavaScript concurrency with yield
Yield is a new feature in ECMAScript 6, it only works on the oldest version of Firefox and Chrome (in Chrome you need to enable Experimental JavaScript appearing in chrome://flags/#enable-javascript-harmony).
The yield keyword causes generator function execution to pause and the value of the expression following the yield keyword is returned to the generator's caller. It can be thought of as a generator-based version of the return keyword.
A generator allows you to suspend execution of a function and resume it later. A generator can be used to schedule your functions with a technique called trampolining.
Here is the example:
var MAX_VALUE = 10000;
Scheduler = {
_tasks: [],
add: function(func){
this._tasks.push(func);
},
start: function(){
var tasks = this._tasks;
var length = tasks.length;
while(length>0){
for(var i=0; i<length; i++){
var res = tasks[i].next();
if(res.done){
tasks.splice(i, 1);
length--;
i--;
}
}
}
}
}
function* updateUI(threadID, maxValue) {
var value = 0;
while(value<=maxValue){
yield document.getElementById("result" + threadID).innerHTML = value;
value++;
}
}
Scheduler.add(updateUI(1, MAX_VALUE));
Scheduler.add(updateUI(2, MAX_VALUE));
Scheduler.add(updateUI(3, MAX_VALUE));
Scheduler.start()
<div id="result1"></div>
<div id="result2"></div>
<div id="result3"></div>
With the HTML5 "side-specs" no need to hack javascript anymore with setTimeout(), setInterval(), etc.
HTML5 & Friends introduces the javascript Web Workers specification. It is an API for running scripts asynchronously and independently.
Links to the specification and a tutorial.
There's no true threading in JavaScript. JavaScript being the malleable language that it is, does allow you to emulate some of it. Here is an example I came across the other day.
There is no true multi-threading in Javascript, but you can get asynchronous behavior using setTimeout() and asynchronous AJAX requests.
What exactly are you trying to accomplish?
Here is just a way to simulate multi-threading in Javascript
Now I am going to create 3 threads which will calculate numbers addition, numbers can be divided with 13 and numbers can be divided with 3 till 10000000000. And these 3 functions are not able to run in same time as what Concurrency means. But I will show you a trick that will make these functions run recursively in the same time : jsFiddle
This code belongs to me.
Body Part
<div class="div1">
<input type="button" value="start/stop" onclick="_thread1.control ? _thread1.stop() : _thread1.start();" /><span>Counting summation of numbers till 10000000000</span> = <span id="1">0</span>
</div>
<div class="div2">
<input type="button" value="start/stop" onclick="_thread2.control ? _thread2.stop() : _thread2.start();" /><span>Counting numbers can be divided with 13 till 10000000000</span> = <span id="2">0</span>
</div>
<div class="div3">
<input type="button" value="start/stop" onclick="_thread3.control ? _thread3.stop() : _thread3.start();" /><span>Counting numbers can be divided with 3 till 10000000000</span> = <span id="3">0</span>
</div>
Javascript Part
var _thread1 = {//This is my thread as object
control: false,//this is my control that will be used for start stop
value: 0, //stores my result
current: 0, //stores current number
func: function () { //this is my func that will run
if (this.control) { // checking for control to run
if (this.current < 10000000000) {
this.value += this.current;
document.getElementById("1").innerHTML = this.value;
this.current++;
}
}
setTimeout(function () { // And here is the trick! setTimeout is a king that will help us simulate threading in javascript
_thread1.func(); //You cannot use this.func() just try to call with your object name
}, 0);
},
start: function () {
this.control = true; //start function
},
stop: function () {
this.control = false; //stop function
},
init: function () {
setTimeout(function () {
_thread1.func(); // the first call of our thread
}, 0)
}
};
var _thread2 = {
control: false,
value: 0,
current: 0,
func: function () {
if (this.control) {
if (this.current % 13 == 0) {
this.value++;
}
this.current++;
document.getElementById("2").innerHTML = this.value;
}
setTimeout(function () {
_thread2.func();
}, 0);
},
start: function () {
this.control = true;
},
stop: function () {
this.control = false;
},
init: function () {
setTimeout(function () {
_thread2.func();
}, 0)
}
};
var _thread3 = {
control: false,
value: 0,
current: 0,
func: function () {
if (this.control) {
if (this.current % 3 == 0) {
this.value++;
}
this.current++;
document.getElementById("3").innerHTML = this.value;
}
setTimeout(function () {
_thread3.func();
}, 0);
},
start: function () {
this.control = true;
},
stop: function () {
this.control = false;
},
init: function () {
setTimeout(function () {
_thread3.func();
}, 0)
}
};
_thread1.init();
_thread2.init();
_thread3.init();
I hope this way will be helpful.
You could use Narrative JavaScript, a compiler that will transforms your code into a state machine, effectively allowing you to emulate threading. It does so by adding a "yielding" operator (notated as '->') to the language that allows you to write asynchronous code in a single, linear code block.
The new v8 engine which should come out today supports it (i think)
In raw Javascript, the best that you can do is using the few asynchronous calls (xmlhttprequest), but that's not really threading and very limited. Google Gears adds a number of APIs to the browser, some of which can be used for threading support.
If you can't or don't want to use any AJAX stuff, use an iframe or ten! ;) You can have processes running in iframes in parallel with the master page without worrying about cross browser comparable issues or syntax issues with dot net AJAX etc, and you can call the master page's JavaScript (including the JavaScript that it has imported) from an iframe.
E.g, in a parent iframe, to call egFunction() in the parent document once the iframe content has loaded (that's the asynchronous part)
parent.egFunction();
Dynamically generate the iframes too so the main html code is free from them if you want.
Another possible method is using an javascript interpreter in the javascript environment.
By creating multiple interpreters and controlling their execution from the main thread, you can simulate multi-threading with each thread running in its own environment.
The approach is somewhat similar to web workers, but you give the interpreter access to the browser global environment.
I made a small project to demonstrate this.
A more detailed explanation in this blog post.
Javascript doesn't have threads, but we do have workers.
Workers may be a good choice if you don't need shared objects.
Most browser implementations will actually spread workers across all cores allowing you to utilize all cores. You can see a demo of this here.
I have developed a library called task.js that makes this very easy to do.
task.js Simplified interface for getting CPU intensive code to run on all cores (node.js, and web)
A example would be
function blocking (exampleArgument) {
// block thread
}
// turn blocking pure function into a worker task
const blockingAsync = task.wrap(blocking);
// run task on a autoscaling worker pool
blockingAsync('exampleArgumentValue').then(result => {
// do something with result
});
With HTML5 specification you do not need to write too much JS for the same or find some hacks.
One of the feature introduced in HTML5 is Web Workers which is JavaScript running in the background,independently of other scripts, without affecting the performance of the page.
It is supported in almost all browsers :
Chrome - 4.0+
IE - 10.0+
Mozilla - 3.5+
Safari - 4.0+
Opera - 11.5+
Topaz is lock free Multithreaded Javascript Engine for .NET: https://github.com/koculu/topaz