implement a new web worker demo

This commit is contained in:
Joel Brandt 2012-12-07 11:14:29 -08:00
parent cc93513e9a
commit b74ad4d993
5 changed files with 198 additions and 129 deletions

View File

@ -1,82 +1,38 @@
<title>Worker</title>
<article>
<p>Canvas is running whilst an prime number finder runs in a worker</p>
<p>Prime found: <span id="status">0</span></p>
<div><input type="button" value="start worker" id="toggleWorker" /></div>
</article>
<script>
buildSpinner({ x : 50, y : 50, size : 20, degrees : 30 });
var w = new Worker('/js/cruncher.js');
addEvent(document.getElementById('toggleWorker'), 'click', function () {
w.postMessage('');
});
w.onmessage = function (event) {
if (event.data && (event.data+"").match(/^log:/i)) {
console.log(event.data.match(/^log:\s*(.*)/)[1]);
} else {
document.querySelector('#status').innerHTML = event.data;
}
}
function buildSpinner(data) {
var canvas = document.createElement('canvas');
canvas.height = 100;
canvas.width = 300;
document.querySelector('article').appendChild(canvas);
var ctx = canvas.getContext("2d"),
i = 0, degrees = data.degrees, loops = 0, degreesList = [];
for (i = 0; i < degrees; i++) {
degreesList.push(i);
}
// reset
i = 0;
// so I can kill it later
window.canvasTimer = setInterval(draw, 1000/degrees);
function reset() {
ctx.clearRect(0,0,100,100); // clear canvas
var left = degreesList.slice(0, 1);
var right = degreesList.slice(1, degreesList.length);
degreesList = right.concat(left);
}
function draw() {
var c, s, e;
var d = 0;
if (i == 0) {
reset();
}
ctx.save();
d = degreesList[i];
c = Math.floor(255/degrees*i);
ctx.strokeStyle = 'rgb(' + c + ', ' + c + ', ' + c + ')';
ctx.lineWidth = data.size;
ctx.beginPath();
s = Math.floor(360/degrees*(d));
e = Math.floor(360/degrees*(d+1)) - 1;
ctx.arc(data.x, data.y, data.size, (Math.PI/180)*s, (Math.PI/180)*e, false);
ctx.stroke();
ctx.restore();
i++;
if (i >= degrees) {
i = 0;
}
}
}
</script>
<html>
<head>
<title>Worker</title>
<style>
body {
font-family: sans-serif;
}
#status {
height: 200px;
max-height: 200px;
border: thin solid black;
overflow-y: scroll;
}
#square {
position: absolute;
left: 0px;
top: 0px;
width: 75px;
height: 75px;
background-color: rgba(0, 0, 220, 0.3);
z-index: -1;
}
</style>
</head>
<body>
<h1>Web Worker Demo</h1>
<p>Works in Chrome, Safari, and Firefox. Web worker portion works in Opera.</p>
<p>Use arrow keys to change the direction of the animated square. The square is animated with <em>requestAnimationFrame</em>.</p>
<p>Click the button below to start or stop the worker.</p>
<div><input type="button" value="start worker" id="toggleWorker" /></div>
<h2>Messages from Worker:</h2>
<div id="status"></div>
<div id="square"></div>
<script src="../js/worker-main.js"></script>
</body>
</html>

View File

@ -1,24 +0,0 @@
var running = false;
onmessage = function (event) {
// doesn't matter what the message is, just toggle the worker
if (running == false) {
running = true;
run();
} else {
running = false;
}
};
function run() {
var n = 1;
search: while (running) {
n += 1;
for (var i = 2; i <= Math.sqrt(n); i += 1)
if (n % i == 0)
continue search;
// found a prime!
postMessage(n);
}
}

68
js/worker-cruncher.js Normal file
View File

@ -0,0 +1,68 @@
/*jslint vars: true, plusplus: true, devel: true, nomen: true, indent: 4, maxerr: 50, browser: true */
/*global postMessage, addEventListener */
(function () {
"use strict";
var WHEN_TO_STOP = 10000000;
var COMPUTE_BLOCK_SIZE = 1000000;
var running = false;
var count = 0;
// We have to compute in blocks of ~1 second of computation in order to make sure
// that we read our message queue occasionally. Worker threads are not preemptive
// (like all JS), so if we don't pause computation to read the message queue, we'll
// be unresponsive to user requests.
function compute(start) {
var n = start;
var i, hasDivisor;
if (!running) { // got a message to stop before this call to compute
postMessage("Stopped!");
} else {
while (n < start + COMPUTE_BLOCK_SIZE) {
hasDivisor = false;
for (i = 2; i <= Math.sqrt(n); i += 1) {
if (n % i === 0) {
hasDivisor = true;
break;
}
}
if (!hasDivisor) {
// found a prime!
count++;
}
n += 1;
}
if (n < WHEN_TO_STOP) {
// allow for event loop to actually forward messages to the worker
setTimeout(function () { compute(n); }, 1);
} else {
// we reached the end
running = false;
postMessage("Done!");
}
}
// Finally, always report how many primes we've found so far
postMessage("Found " + count + " primes between 2 and " + (n - 1));
}
addEventListener('message', function (event) {
// doesn't matter what the message is, just toggle the worker
if (running === false) {
postMessage("Starting...");
count = 0;
running = true;
compute(1);
} else {
postMessage("Stopping...");
running = false;
}
});
}());

92
js/worker-main.js Normal file
View File

@ -0,0 +1,92 @@
/*jslint vars: true, plusplus: true, devel: true, nomen: true, indent: 4, maxerr: 50, browser: true */
/*global Worker */
(function () {
"use strict";
var SQUARE_SIZE = 75;
var MOVEMENT_STEP = 3;
var requestAnimationFrame = window.requestAnimationFrame || window.mozRequestAnimationFrame ||
window.webkitRequestAnimationFrame || window.msRequestAnimationFrame;
// Set up the worker
var running = false;
var statusDiv = document.getElementById('status');
var button = document.getElementById('toggleWorker');
var worker = new Worker('../js/worker-cruncher.js');
worker.addEventListener('message', function (event) {
var currentStatus = statusDiv.innerHTML;
statusDiv.innerHTML = "<p>" + event.data + "</p>" + currentStatus;
if (event.data === "Done!") {
running = false;
button.value = "start worker";
}
});
button.onclick = function () {
running = !running;
if (running) {
statusDiv.innerHTML = "";
button.value = "stop worker";
} else {
button.value = "start worker";
}
worker.postMessage('');
};
// Set up the animated square
var square = document.getElementById('square');
var direction = 39; // right
square.style.top = 0;
square.style.left = 20;
square.style.height = SQUARE_SIZE;
square.style.width = SQUARE_SIZE;
function moveSquare() {
var left = parseInt(square.style.left, 10);
var top = parseInt(square.style.top, 10);
var right = left + SQUARE_SIZE;
var bottom = top + SQUARE_SIZE;
switch (direction) {
case 37: // left
if (left > 0) {
square.style.left = left - MOVEMENT_STEP;
}
break;
case 38: // up
if (top > 0) {
square.style.top = top - MOVEMENT_STEP;
}
break;
case 39: //right
if (right < document.documentElement.clientWidth) {
square.style.left = left + MOVEMENT_STEP;
}
break;
case 40: // down
if (bottom < document.documentElement.clientHeight) {
square.style.top = top + MOVEMENT_STEP;
}
break;
default:
break;
}
requestAnimationFrame(moveSquare);
}
window.onkeydown = function (event) {
if (event.keyCode >= 37 && event.keyCode <= 40) { // is an arrow key
direction = event.keyCode;
}
};
// start the square animating
requestAnimationFrame(moveSquare);
}());

View File

@ -1,23 +0,0 @@
var running = false;
var ctr = 0;
function log(s) {
postMessage('log:' + s);
}
onmessage = function (event) {
// doesn't matter what the message is, just toggle the worker
if (running == false) {
running = true;
run();
} else {
running = false;
}
};
function run() {
while (running) {
postMessage(ctr);
ctr++;
}
}