|
<h1 id="web-workers">Web Workers</h1> |
|
<h2 id="tldr-tldh">TL;DR (TL;DH)</h2> |
|
<blockquote> |
|
<p>Web Workers are a simple means for web content to run scripts in background threads.<br /> |
|
The worker thread can perform tasks without interfering with the user interface.</p> |
|
</blockquote> |
|
<h2 id="what-is-a-web-worker">What is a Web Worker?</h2> |
|
<p>A web worker is just a javascript <em>context</em> (i.e. webpage subprocess) that runs inside the browser<br /> |
|
main process, without blocking other javascript <em>contexts</em>.</p> |
|
<p>Those contexts can interact with other contexts via <a href="https://developer.mozilla.org/en-US/docs/Web/API/Web_Workers_API"><em>Web Workers API</em></a>.</p> |
|
<div class="sourceCode"><pre class="sourceCode javascript"><code class="sourceCode javascript"> __________________________________________________________ |
|
<span class="op">|</span>BROWSER MAIN PROCESS <span class="op">|</span> |
|
<span class="op">|</span> _________________________________________________ <span class="op">|</span> |
|
<span class="op">|</span> <span class="op">|</span>WEBPAGE PROCESS <span class="op">|</span> <span class="op">|</span> |
|
<span class="op">|</span> <span class="op">|</span> _____________ _____________ <span class="op">|</span> <span class="op">|</span> |
|
<span class="op">|</span> <span class="op">|</span> <span class="op">|</span> <span class="op">|</span> <span class="op">|</span> <span class="op">|</span> <span class="op">|</span> <span class="op">|</span> |
|
<span class="op">|</span> <span class="op">|</span> <span class="op">|</span> JS context <span class="op">|</span> [WW Api] <span class="op">|</span> JS context <span class="op">|</span> <span class="op">|</span> <span class="op">|</span> |
|
<span class="op">|</span> <span class="op">|</span> <span class="op">|</span> (thread) <span class="op">|</span> <span class="op"><------></span> <span class="op">|</span> (thread) <span class="op">|</span> <span class="op">|</span> <span class="op">|</span> |
|
<span class="op">|</span> <span class="op">|</span> <span class="op">|</span>_____________<span class="op">|</span> <span class="op">|</span>_____________<span class="op">|</span> <span class="op">|</span> <span class="op">|</span> |
|
<span class="op">|</span> <span class="op">|</span> <span class="op">|</span> <span class="op">|</span> |
|
<span class="op">|</span> <span class="op">|</span>_________________________________________________<span class="op">|</span> <span class="op">|</span> |
|
<span class="op">|</span> <span class="op">|</span> |
|
<span class="op">|</span>__________________________________________________________<span class="op">|</span></code></pre></div> |
|
<p>The web workers in any of their flavors are thread-safe, meaning that it's pretty weird to have<br /> |
|
concurrency problems between two javascript <em>contexts</em>.</p> |
|
<p>In order to have two javascript <em>contexts</em> to communicate between them, certain requirements needs<br /> |
|
to be met (<em>in the web development land</em>):</p> |
|
<ul> |
|
<li>One javascript context must act as sender and the other as receiver.</li> |
|
<li>All js receivers must be within the same origin as the js sender or diff origin must allow external access (CORS madness).</li> |
|
<li>A receiver can be used by more than one js sender, as long as the js receiver code allows it (SharedWebWorker).</li> |
|
<li>At least one sender must be created by a webpage subprocess.</li> |
|
<li>A shared worker <strong>must</strong> share the same origin (i.e. protocol, host, port) as the sender (Diff flavor of CORS madness).</li> |
|
</ul> |
|
<h2 id="code-examples">Code examples</h2> |
|
<h3 id="dedicated-web-workers.">Dedicated web workers.</h3> |
|
<p>Here's the javascript code that is istantiated by a webpage.</p> |
|
<div class="sourceCode"><pre class="sourceCode javascript"><code class="sourceCode javascript"><span class="co">// main.js</span> |
|
<span class="cf">if</span> (<span class="op">!</span><span class="va">window</span>.<span class="at">Worker</span>) <span class="op">{</span> |
|
<span class="cf">throw</span> <span class="st">'Web workers not supported'</span><span class="op">;</span> |
|
<span class="op">}</span> |
|
|
|
<span class="co">// Let's create a dedicated web worker instance.</span> |
|
<span class="kw">const</span> myWorker <span class="op">=</span> <span class="kw">new</span> <span class="at">Worker</span>(<span class="st">'a-worker.js'</span>)<span class="op">;</span> |
|
|
|
<span class="co">// When I click the button</span> |
|
<span class="va">document</span>.<span class="at">querySelector</span>(<span class="st">'#clickme'</span>).<span class="at">addEventListener</span>(<span class="st">'click'</span><span class="op">,</span> (evt) <span class="op">=></span> <span class="op">{</span> |
|
<span class="co">// Send the event object that is created for this listener to the web worker.</span> |
|
<span class="co">// The type of the data that we can send is any javascript object or value that can be</span> |
|
<span class="co">// deep cloned (see list here: https://developer.mozilla.org/en-US/docs/Web/API/Web_Workers_API/Structured_clone_algorithm).</span> |
|
<span class="va">myWorker</span>.<span class="at">postMessage</span>(<span class="op">{</span> <span class="dt">argument</span><span class="op">:</span> evt <span class="op">}</span>)<span class="op">;</span> |
|
<span class="op">}</span>)<span class="op">;</span> |
|
|
|
<span class="co">// everytime the worker wants to speak to us.</span> |
|
<span class="va">myWorker</span>.<span class="at">onMessage</span> <span class="op">=</span> (evt) <span class="op">=></span> <span class="op">{</span> |
|
<span class="at">alert</span>(<span class="va">evt</span>.<span class="at">data</span>)<span class="op">;</span> |
|
<span class="va">console</span>.<span class="at">log</span>(<span class="st">'an alert has been issued with the data received'</span>)<span class="op">;</span> |
|
<span class="op">};</span></code></pre></div> |
|
<p>and here's the worker code:</p> |
|
<div class="sourceCode"><pre class="sourceCode javascript"><code class="sourceCode javascript"><span class="co">//a-worker.js</span> |
|
|
|
<span class="co">// This is the same as DedicatedWorkerGlobalScope.onmessage</span> |
|
onmessage <span class="op">=</span> (evt) <span class="op">=></span> <span class="op">{</span> |
|
<span class="co">// We cannot use alert, because: 1. It's a background process. 2. alert == window.alert.</span> |
|
<span class="va">console</span>.<span class="at">log</span>(<span class="st">'The sender talked to us!'</span>)<span class="op">;</span> |
|
|
|
<span class="kw">let</span> theWords <span class="op">=</span> <span class="va">evt</span>.<span class="at">data</span><span class="op">;</span> <span class="co">// Since we sen an object, theWords should be a **copy** of that.</span> |
|
<span class="va">console</span>.<span class="at">log</span>(<span class="st">'the spoken words'</span><span class="op">,</span> <span class="va">theWords</span>.<span class="at">argument</span>)<span class="op">;</span> |
|
|
|
<span class="at">postMessage</span>(<span class="st">'I CAN HEAR YOU FINE'</span>)<span class="op">;</span> |
|
<span class="op">};</span></code></pre></div> |
|
<h3 id="shared-web-workers">Shared Web workers</h3> |
|
<p>The code differs a bit from the dedicated web worker, in the sense that now we need to keep track<br /> |
|
of a <em>port</em> which is unique per shared connection, so let's see the "live" example.</p> |