Worker
My Goal
WORKER, OMG WOW
Content
- Web Worker (now)
- Shared Worker (soon)
- Service Worker (far far away)
Worker
Reason
- Run a task without blocking the current thread
- That's it
- Support: 93%
Demo time
(sponsored by D1)
How to start
if (window.Worker) {
var myWorker = new Worker('worker.js');
} else {
// :(
}
also possible inline via type="text/js-worker"
How to interact
myWorker.postMessage(['your','fancy', 'workload']);
myWorker.onmessage = function(e) {
console.log('Message received from worker', e.data);
}
myWorker.terminate(); // No cleanup at worker
How to worker
importScripts('foo.js'); // import lib
onmessage = function(e) {
var workerResult =
'Result: ' +
e.data[0] +
e.data[1] +
'Result';
console.log('Posting message back to main script');
postMessage(workerResult);
close(); // Terminate process gracefully
}
Shared Worker
What's better?
- Shares context with multiple windows / iframes
- heavy init works can be done only once
- Communication differs to normal worker
- support: 56%
Demo time!
worker.js
var clients = [];
var broadcast = function(clients, message) {..clients[i].postMessage(message);..}
self.addEventListener("connect", function (e) {
var port = e.ports[0];
clients.push(port);
port.addEventListener("message", function (e) {
var data = e.data;
broadcast(clients, {"id": e.data.id, "said": e.data.message});
})
port.start();
broadcast(clients, {"id":clients.length, "cmd": "connected"});
}, false);
page.js
var worker = new SharedWorker("./sharedWorker.js");
var id = null;
worker.port.addEventListener("message", function(e) {
console.log('worker id is', id)
if (e.data.cmd === 'connected' && !(id != null)) {
id = e.data.id;
} else {
$('#chat-window').append('<div>' + JSON.stringify(e.data) + '</div>');
}
});
worker.port.start();
$('#dialog-form').on('submit', function(e){
...
worker.port.postMessage({message: message, id: id});
});
Service Worker
Service Worker
Why is it awesome?
- it lives in a context above the page
- it can intercept with network requests
- it has it's own cache
- it has it's own lifecycle
- it can be scoped
Lifecycle
Where can I look into the internals?
Registration
if ('serviceWorker' in navigator) {
navigator.serviceWorker.register('/sw.js').then(function() {
// Registration was successful
console.log('ServiceWorker registration successful');
}).catch(function() {
// registration failed :(
console.log('ServiceWorker registration failed');
});
}
But how do I update a service worker?
Change the File ==> Phased Restart
- background loading and diff checking
- install event will be fired on new one
- current one on waiting state
- kill old service worker instances on window close
Installation
var CACHE_NAME = 'my-site-cache-v1';
var urlsToCache = [
'/',
'/styles/main.css',
'/script/main.js'
];
self.addEventListener('install', function(event) {
// Perform install steps
event.waitUntil(
caches.open(CACHE_NAME)
.then(function(cache) {
console.log('Opened cache');
return cache.addAll(urlsToCache);
})
);
});
Fetch
self.addEventListener('fetch', function(event) {
event.respondWith(
caches.match(event.request)
.then(function(response) {
// Cache hit - return response
if (response) {
return response;
}
// IMPORTANT: Clone the request. A request is a stream and
// can only be consumed once. Since we are consuming this
// once by cache and once by the browser for fetch, we need
// to clone the response
var fetchRequest = event.request.clone();
return fetch(fetchRequest).then(function(response) {
// Check if we received a valid response
if(!response || response.status !== 200 || response.type !== 'basic') {
return response;
}
Fetch
// Check if we received a valid response
if(!response || response.status !== 200 || response.type !== 'basic') {
return response;
}
// IMPORTANT: Clone the response. A response is a stream
// and because we want the browser to consume the response
// as well as the cache consuming the response, we need
// to clone it so we have 2 stream.
var responseToCache = response.clone();
caches.open(CACHE_NAME)
.then(function(cache) {
cache.put(event.request, responseToCache);
});
return response;
}
);
})
);
});
Activate
self.addEventListener('activate', function(event) {
var cacheWhitelist = ['pages-cache-v1', 'blog-posts-cache-v1'];
event.waitUntil(
caches.keys().then(function(cacheNames) {
return Promise.all(
cacheNames.map(function(cacheName) {
if (cacheWhitelist.indexOf(cacheName) === -1) {
return caches.delete(cacheName);
}
})
);
})
);
});
Let's talk about the scope
- /
- /mobile
- /posts
- /events
- /frontend
- /posts
- /events
- /mobile
Let's talk about the scope
-
/
-
/mobile
- /posts
- /events
-
/frontend
- /posts
- /events
-
/mobile
navigator.serviceWorker.register('serviceWorker.js')
Let's talk about the scope
- /
- /mobile
- /posts
- /events
-
/frontend
- /posts
- /events
- /mobile
navigator.serviceWorker.register('serviceWorker.js')
Let's talk about the scope
- /
- /mobile
- /posts
- /events
-
/frontend
- /posts
- /events
- /mobile
navigator.serviceWorker.register('serviceWorker.js', {
scope: '/frontend'
})
Cache API
- on Cache Storage
- match, open
- has, keys
- delete
- on Cache
- match(All), add(All)
- put, delete, keys
(Returns promises) - polyfill here
Downsides
- fetch doesn't follow redirects
- Handling of srcset is difficult
- little bug with hash in url
- it's not that ready
Current Status
Planned Features
Demo time
Worker
By Daniel Schmidt
Worker
Welcome to the land of awesomness :)
- 1,107