Optimera React med Web workers

Optimera React med Web workers

Webben har utvecklats massor sedan www slog igenom i mitten av 90-talet. En sak som är värd att komma ihåg är att en webbläsare fortfarande inte kan uppdatera vad som visas så länge huvudtråden (“main thread”) är upptagen. Det kan innebära att sidan ser ut att ha hängt sig.

Det har självklart kommit olika lösningar på problemet. Exempelvis att köra kod asynkront – detta fungerar bra med liten och enkel kod som inte behandlar stor mängder data – som att hämta uppdateringar baserat på ändringar i gränssnittet eller vänta på svar från en enkel serverfråga. Om det rör sig om mer komplexa frågor och/eller stora datamängder fungerar däremot inte asynkron kod lika bra.

Visste du förresten att JSON.parse blockerar DOM vilket kan få gränssnittet att frysa?
Ju större datamängder som behandlas desto större är risken att webbläsaren fryser – eftersom anrop mot DOM går parallellt med Javascript i huvudtråden.

Web worker

En Web worker är ett enkelt sätt att köra kod i bakgrunden – i separata trådar. Det är värt att komma ihåg att web workers inte har tillgång till DOM eftersom DOM inte är “thread safe”. Att göra DOM:en tillgänglig skulle alltså motverka syftet med web workers.

Comlink

GoogleChromeLabs/Comlink är ett enkelt bibliotek för implementation av web workers.

Det enda sättet att kommunicera mellan huvudprogrammet (“main thread”) och en web worker är genom “postMessage”. Comlink gör det enklare att skriva en klass i en web worker och eponera den mot huvudprogrammet (“main thread”).

Skapa en index.html som laddar index.js
<!DOCTYPE html>
<html lang=”en”>
<head>
<meta charset=”UTF-8″>
<meta name=”viewport” content=”width=device-width, initial-scale=1.0″>
<title>Comlink Fetch</title>
</head>
<body>
<script type=”module” src=”index.js”></script>
</body>
</html>

JavaScript modules (type=”module”) används för att möjliggöra import Comlink inifrån index.js

Skapa en index.js med nedan kod:
import { Comlink } from ‘./node_modules/comlinkjs/comlink.es6.js’;
const worker = new Worker(‘./node_modules/comlink-fetch/src/fetch.worker.js’);
const proxy = Comlink.proxy(worker);

async function getSecondaryInfo() {
const API = await new proxy.Fetch;

API.setBaseUrl(“https://jsonplaceholder.typicode.com/”);
API.setDefaultHeaders({ ‘Content-Type’: ‘application/json’ });
API.setDefaultBody({ lang: ‘en’ });

let page1 = API.get(‘posts/1’);
let page2 = API.get(‘posts/2’);

console.log(await page1);
console.log(await page2);
}

getSecondaryInfo();

Det här är vad som händer i index.js:

Comlink importeras från comlink.es6.js
En ny web worker skapas från  comlink-fetch fetch.worker.js
En proxy skapas för web workern med hjälp av Comlink

Det är allt som krävs! Resten av koden är ett exempel där information hämtas i bakgrunden, exempelvis blog-inlägg. Att ladda dem i end web worker säkerställer att de inte blockerar huvudtråden (“main thread”)!

En sak som är värd att notera är att GET “/posts/1” och GET “/posts/2” körs parallellt samtidigt i bakgrunden.

Prestandafördelar

Så, kommer du behöva använda web workers för allt nu? Nej, verkligen inte. 🙂
Men om du måste utföra omfattande bearbetning av hämtat data – då är det en bra idé att titta på web workers!