Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
0 parents
commit aad6a39
Showing
6 changed files
with
234 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,56 @@ | ||
Slowloris | ||
========= | ||
|
||
Slowloris is a very simple web worker with the following goals: | ||
|
||
+ Get non-UI logic out of the UI thread. This is often one of the main causes of underlying client-side performance problems. | ||
+ Safety and simplicity: Normal use of Slowloris should not cause any concurrency nightmares (deadlock, race conditions, etc). | ||
+ Legacy compatibility: Older browsers won't break and newer browsers will magically get faster. | ||
+ Ignoring web workers: You shouldn't really need to write a web worker yourself unless you have a special use case. | ||
|
||
I've included an example in the repo that has a bit of information about it. | ||
|
||
Basically, you get an asynchronous eval() function. This is a pretty bad way to write real code; instead you should build a | ||
better abstraction on top of Slowloris to offload as much as you can into the background thread and only do UI updates once | ||
the computation is complete. | ||
|
||
HOWTO | ||
----- | ||
|
||
See the example. Basically all you need to know is Loris.eval(expr, callback, errback); where expr is a string containing | ||
the expression to evaluate, callback is the function that gets called with the result of the eval, and errback gets called | ||
with any exceptions that may get thrown. | ||
|
||
FAQ | ||
--- | ||
|
||
+ The example isn't working. | ||
|
||
Web workers don't work locally; use a web server. Read the paragraph in the example. | ||
|
||
+ Why do I only get eval? | ||
|
||
Because it's flexible enough to build a good abstraction on, but doesn't encourage you to actually do "real" concurrency. | ||
|
||
+ Why do I only get 1 background thread? | ||
|
||
Because the point of this is not to utilize multiple cores or be a great framework for building parallel apps, it's just | ||
designed to get as much crap out of the UI thread as possible so you can build really responsive client-side apps in JS. | ||
|
||
+ What should I avoid? | ||
|
||
Just don't touch the underlying web worker stuff (Loris.worker or call any web worker stuff in the string you eval). | ||
|
||
+ What's next? | ||
|
||
I'd like to build an AMD module loader on top of this so you can just pass a module name and function name rather than a | ||
string of JS to eval. | ||
|
||
+ Why did you build this? | ||
|
||
Because it's a shame that the web stack has such a bad reputation on mobile vs. native. Sure, native will probably always | ||
beat web on benchmarks, but it shouldn't beat the web stack by as much as it has been. I think this is mostly due to | ||
the fact that web front-end engineers put too much computation in the UI thread. There's a few other candiates too: | ||
inefficient CSS (and JS that drives the CSS!) and poor network fetching/batching/caching of data and code. Hopefully | ||
Slowloris solves the first problem, education and something like Zepto solves the second one, and something like Backbone | ||
solves the third problem. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,23 @@ | ||
#supported, | ||
#time, | ||
#status, | ||
#computetime { | ||
font-weight: bold; | ||
} | ||
|
||
#timeblock { | ||
font-size: 24px; | ||
} | ||
|
||
.code { | ||
font-family: monospace; | ||
} | ||
|
||
#localwarning { | ||
color: red; | ||
display: none; | ||
} | ||
|
||
.runninglocal #localwarning { | ||
display: block; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,42 @@ | ||
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" | ||
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> | ||
|
||
<html xmlns="http://www.w3.org/1999/xhtml"> | ||
|
||
<head> | ||
|
||
<title>Pointless Slowloris demo</title> | ||
<script type="text/javascript" src="slowloris.js"></script> | ||
<script type="text/javascript" src="http://ajax.aspnetcdn.com/ajax/jQuery/jquery-1.8.1.min.js"></script> | ||
<script type="text/javascript" src="slowloris-example.js"></script> | ||
<link rel="stylesheet" href="example.css" /> | ||
</head> | ||
|
||
<body> | ||
<h1>Slowloris demo</h1> | ||
<p id="localwarning"><b>Web workers aren't available for local files!</b> | ||
Please load this from a web server (http:// or https:// URL), not locally (file:// URL). An easy way to do this is | ||
just running <span class="code">python -m SimpleHTTPServer</span> and visiting | ||
<a href="http://localhost:8000/example.html">http://localhost:8000/example.html</a>. | ||
</p> | ||
<p> | ||
This is an interactive demo of how Slowloris can improve your application performance. | ||
|
||
I've written a simple clock to represent an interactive UI. | ||
|
||
And I've also written a terrible for loop that blocks the UI thread. This represents "computation". | ||
|
||
I apologize if this messes up your browser. In fact, if you're using a browser that doesn't support web workers | ||
you are probably already screwed; sorry! | ||
|
||
Also look out, this spins your CPU and could kill your battery life on a laptop or mobile device. | ||
|
||
Once you disable web workers, notice how choppy the clock updates itself. Voila! | ||
</p> | ||
<p id="timeblock">The current time is: <span id="time"> </span>.</p> | ||
<p>Last result computed at: <span id="computetime">never</span>.</p> | ||
<p>Web workers <span id="supported"> </span> supported by this browesr.</p> | ||
<p>Web workers are <span id="status"> </span>.</p> | ||
<p><a href="javascript:;" id="toggle">Toggle web workers</a>.</p> | ||
</body> | ||
</html> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,36 @@ | ||
$(document).ready(function() { | ||
// Set up event handlers and init the UI | ||
$('#toggle').click(function() { | ||
if (Loris.usingWebWorkers()) { | ||
Loris.disableWebWorkers(); | ||
} else { | ||
Loris.enableWebWorkers(); | ||
} | ||
$('#status').text(Loris.usingWebWorkers() ? 'enabled' : 'disabled'); | ||
}); | ||
$('#supported').text(Loris.hasWebWorkers() ? 'are' : 'are not'); | ||
$('#status').text(Loris.usingWebWorkers() ? 'enabled' : 'disabled'); | ||
if (window.location.href.substring(0, 4).toLowerCase() !== 'http') { | ||
$('body').addClass('runninglocal'); | ||
} | ||
|
||
function tick() { | ||
// Update the clock and run some asynchronous computation. | ||
$('#time').text(new Date()); | ||
Loris.eval('var sum = 0; for (var i = 0; i < 10000000; i++) { sum++; }; [sum, new Date];', function (result) { | ||
// Let's pretend we actually do something with the result here. We'll check its value here to | ||
// prove that it's working, and update the UI to prove that we can update it. | ||
var sum = result[0]; | ||
var date = result[1]; | ||
if (sum !== 10000000) { | ||
$('#computetime').text('there was an error :('); | ||
} else { | ||
$('#computetime').text(date); | ||
} | ||
}); | ||
} | ||
tick(); | ||
|
||
// Keep the clock updated | ||
setInterval(tick, 500); | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
self.onmessage = function (event) { | ||
try { | ||
self.postMessage([event.data[0], true, eval(event.data[1])]); | ||
} catch (e) { | ||
self.postMessage([event.data[0], false, e]); | ||
} | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,70 @@ | ||
window.Loris = (function() { | ||
var hasWebWorkers = typeof(Worker) !== 'undefined'; | ||
var Loris = function() { | ||
this.callbacks = {}; | ||
this.errbacks = {} | ||
this.counts = 0; | ||
this.worker = null; | ||
this.useWebWorkers = hasWebWorkers; | ||
if (hasWebWorkers) { | ||
try { | ||
this.worker = new Worker('slowloris-webworker.js'); | ||
} catch (e) { | ||
hasWebWorkers = false; | ||
this.useWebWorkers = false; | ||
return; | ||
} | ||
this.worker.onmessage = (function (event) { | ||
var id = event.data[0]; | ||
var isException = event.data[1]; | ||
var payload = event.data[2]; | ||
|
||
if (isException) { | ||
this.callbacks[id](payload); | ||
} else { | ||
this.errbacks[id](payload); | ||
} | ||
delete this.callbacks[id]; | ||
delete this.errbacks[id]; | ||
}).bind(this); | ||
this.worker.onerror = function(event) { | ||
// This should never actually happen but let's log it anyway | ||
console.log('Slowloris uncaught exception: ', event.data); | ||
}; | ||
} | ||
}; | ||
|
||
Loris.prototype.eval = function (expr, callback, errback) { | ||
if (!this.useWebWorkers) { | ||
// TODO: explore trampolining this eval with setTimeout(). | ||
try { | ||
callback(eval(expr)); | ||
} catch (e) { | ||
errback(e); | ||
} | ||
return; | ||
} | ||
var id = this.counts++; | ||
this.callbacks[id] = callback; | ||
this.errbacks[id] = errback; | ||
this.worker.postMessage([id, expr]); | ||
}; | ||
|
||
Loris.prototype.disableWebWorkers = function() { | ||
this.useWebWorkers = false; | ||
}; | ||
|
||
Loris.prototype.enableWebWorkers = function() { | ||
this.useWebWorkers = hasWebWorkers; | ||
}; | ||
|
||
Loris.prototype.usingWebWorkers = function() { | ||
return this.useWebWorkers; | ||
}; | ||
|
||
Loris.prototype.hasWebWorkers = function() { | ||
return hasWebWorkers; | ||
}; | ||
|
||
return new Loris(); | ||
})(); |