Definindo e Implementando Web Workers
Introdução
JavaScript é uma linguagem de execução single-threaded, o que significa que apenas um script é interpretado e executado de cada vez. A execução é linear e sequencial, o que implica que eventos, manipuladores de eventos e callbacks não podem ser processados simultaneamente.
Por exemplo, ao enviar uma requisição Ajax para o servidor, o script que dispara a solicitação continua executando até que o servidor responda. Quando a resposta chega, o callback associado é executado, interrompendo temporariamente a execução do código ao redor. Esse modelo pode criar gargalos em operações mais intensivas, que impactam diretamente a responsividade do aplicativo.
A Web Worker API resolve essas limitações ao permitir a execução de tarefas em segundo plano. Web workers utilizam threads separadas para processar informações de maneira paralela, garantindo que o thread principal permaneça disponível para operações críticas, como a interação do usuário.
Criando um Web Worker
Para criar um Web Worker, utiliza-se o construtor Worker:
const worker = new Worker("worker.js");
O script que o worker executa deve estar em um arquivo separado. Esse caminho é passado como parâmetro para o construtor Worker
O Web Worker criado com a função Worker()
Nota: Para que o Web Worker funcione corretamente, tanto a página web quanto os arquivos do Web Worker devem ser servidos por um servidor web. Neste exemplo, usaremos o Node.js como servidor por simplicidade, mas qualquer outra tecnologia de servidor pode ser utilizada.
Exemplo Prático
Criaremos uma pasta no disco com os seguintes arquivos:
: Página principal da aplicação.index.html
: Script executado pelo Web Worker.worker.js
: Código para o servidor Node.js.server.js
Definindo a Página Web e Criando o Web Worker
No arquivo index.html
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title>Web Worker Example</title>
</head>
<body>
<script>
const worker = new Worker("worker.js");
</script>
</body>
</html>
Aqui, apenas criamos uma instância do Web Worker que executará o código no arquivo worker.js
Definindo o Código do Web Worker
No arquivo worker.js
let result = 1;
const intervalID = setInterval(work, 1000);
function work() {
result = result * 2;
console.log("result =", result);
if (result >= 32) clearInterval(intervalID);
}
Este código usa a função setInterval()
work
result
result
Definindo o Servidor
Para servir a página corretamente, precisaremos de um servidor web. No arquivo server.js
const http = require("http");
const fs = require("fs");
http.createServer((request, response) => {
let filePath = request.url.substring(1);
if (!filePath) filePath = "index.html";
response.setHeader("Content-Type", "text/html; charset=utf-8;");
fs.readFile(filePath, (error, data) => {
if (error) {
response.statusCode = 404;
response.end("<h1>Resource not found!</h1>");
} else {
response.end(data);
}
});
}).listen(3000, () => console.log("Servidor iniciado em http://localhost:3000"));
Vamos analisar o código. Primeiro, importamos os pacotes que oferecem as funcionalidades necessárias:
const http = require("http"); // para processar solicitações recebidas
const fs = require("fs"); // para ler arquivos do sistema de arquivos
Para criar o servidor, utilizamos a função http.createServer()
request
response
Na função de callback, podemos usar a propriedade request.url para obter o caminho do recurso solicitado. O objetivo é processar requisições para páginas como index.html
home.html
/
home.html
/home.html
let filePath = request.url.substring(1);
Caso a solicitação seja direcionada para a raiz do site (apenas /
index.html
if (!filePath) filePath = "index.html";
Como o servidor retornará HTML neste caso, utilizamos o método setHeader()
text/html
response.setHeader("Content-Type", "text/html; charset=utf-8;");
Em seguida, usamos a função fs.readFile
server.js
fs.readFile(filePath, (error, data) => {
if (error) { // em caso de erro
response.statusCode = 404;
response.end("<h1>Resource not found!</h1>");
}
Caso não haja erro, o conteúdo do arquivo é enviado como resposta:
else {
response.end(data);
}
Por fim, iniciamos o servidor na porta 3000 utilizando a função listen()
http://localhost:3000/
.listen(3000, () => console.log("Servidor iniciado em http://localhost:3000"));
Executando o Servidor
Agora, no terminal, navegamos até a pasta onde está o servidor utilizando o comando cd
node server.js
C:\app>node server.js Servidor iniciado em http://localhost:3000
Depois de iniciar o servidor, abrimos o navegador e acessamos o endereço http://localhost:3000
result = 2 result = 4 result = 8 result = 16 result = 32
Limitações dos Web Workers
No exemplo acima, o Web Worker utilizou um temporizador criado com a função setInterval()
window
window
setInterval(
A seguir, uma lista de funções que podem ser usadas em Web Workers:
atob()
btoa()
clearInterval()
clearTimeout()
queueMicrotask()
setInterval()
setTimeout()
structuredClone()
(apenas para Web Workers dedicados)requestAnimationFrame()
(apenas para Web Workers dedicados)cancelAnimationFrame()
Além disso, os seguintes objetos e propriedades do objeto window
console
location
navigator
indexedDB
Barcode Detection API
Broadcast Channel API
Cache API
Channel Messaging API
Console API
Web Crypto API (como Crypto)
CSS Font Loading API
CustomEvent
Encoding API (como TextEncoder e TextDecoder)
Fetch API
File API
FileReader
FormData
ImageBitmap
ImageData
IndexedDB
Media Source Extensions API
Network Information API
Notifications API
OffscreenCanvas e APIs relacionadas ao contexto de elementos
canvas
Performance API
Server-Sent Events
ServiceWorkerRegistration
URL API
WebCodecs API
WebSocket
XMLHttpRequest
Web Workers também podem acessar várias APIs do navegador, como:
Utilizando self em Web Workers
Dentro do script de um Web Worker, podemos acessar o objeto do próprio Web Worker usando a palavra-chave self
console.log(self); // exibe dados sobre o Web Worker
let result = 1;
const intervalID = setInterval(work, 1000);
function work() {
result = result * 2;
console.log("result =", result);
if (result >= 32) clearInterval(intervalID);
}
Nesse caso, self
Interrompendo um Web Worker
Um Web Worker pode continuar funcionando indefinidamente enquanto o usuário permanece na página. Para finalizar a execução de um Web Worker, o método terminate()
Worker
index.html
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title>www.programicio.com</title>
</head>
<body>
<button id="btn">Stop</button>
<script>
const worker = new Worker("worker.js");
// Ao clicar no botão, o Web Worker será interrompido
document.getElementById("btn").addEventListener("click", () => {
worker.terminate();
console.log("Web Worker interrompido");
});
</script>
</body>
</html>
Aqui, um botão é adicionado à página web. Ao clicar no botão, o Web Worker é interrompido com o método terminate()