Trabalhadores da Web em ação: por que eles são úteis e como você deve usá-los

The Hungry Brain Blocked Desbloquear Seguir Seguindo 12 de dezembro de 2018 Foto de Fabian Grohs em Unsplash

Javascript é single threaded e vários scripts não podem ser executados ao mesmo tempo. Portanto, se executarmos qualquer tarefa de computação pesada, às vezes nossa página não responderá e o usuário não poderá fazer mais nada até que a execução seja concluída.

Por exemplo:

código de bloqueio

No exemplo acima, se você chamar a média antes do método hello , sua página não responderá e você não poderá clicar em Hello até que a execução da média seja concluída.

Você pode ver que quando a média é chamada com 10000 como entrada primeiro, levou ~ 1,82 segundos. Durante esse período, a página não responde e você não pode clicar no botão "Olá".

Programação Assíncrona

O Javascript fornece aos desenvolvedores uma maneira de escrever códigos assíncronos . Ao escrever um código assíncrono, você pode evitar esse tipo de problema em seu aplicativo. Ele permite que a interface do usuário do seu aplicativo seja responsiva ao "agendar" partes do código a serem executadas um pouco mais tarde no loop de eventos.

Um bom exemplo de programação assíncrona é uma solicitação XHR. Neste encontramos uma API de forma assíncrona e, enquanto aguardamos a resposta, outro código pode ser executado. Mas isso é limitado a determinados casos de uso relacionados principalmente a APIs da web.

Outra maneira de escrever código assíncrono é usando o setTimeout método. Em alguns casos, você pode obter bons resultados ao desbloquear a interface do usuário de cálculos de execução mais longa usando setTimeout. Por exemplo, você pode fazer isso agrupando um cálculo complexo em chamadas setTimeout separadas.

Por exemplo:

programação assíncrona usando setTimeout

Neste exemplo, você pode ver que depois de clicar no botão Calcular Média , você ainda pode clicar no botão Olá (que por sua vez mostra uma mensagem de alerta). Este modo de programação é certamente não-bloqueante, mas leva muito tempo e não é viável em aplicações do mundo real.

Aqui, para a mesma entrada 10000, demorou ~ 60 segundos, o que é muito ineficiente.

Então, como resolvemos esses problemas de maneira eficiente?

A resposta é Web Workers.

O que são trabalhadores da web?

Trabalhadores da Web em Javascript são uma ótima maneira de executar alguma tarefa que é muito trabalhosa e leva muito tempo em um thread separado do thread principal. Eles são executados em segundo plano e executam tarefas sem interferir na interface do usuário.

Os Web Workers não fazem parte do JavaScript. Eles são um recurso do navegador que pode ser acessado através do JavaScript.

Web workers são criados por uma função construtora Worker () que executa um arquivo JS nomeado.

main.js

Se o arquivo especificado existir, ele será baixado de forma assíncrona. Caso contrário, o responsável falhará silenciosamente, portanto, seu aplicativo ainda funcionará no caso de um erro 404.

Vamos aprender mais sobre a criação de web workers e como eles funcionam na próxima seção.

Um thread de trabalho tem seu próprio contexto e, portanto, você só pode acessar os recursos selecionados dentro de um thread de trabalho, como soquetes da Web e banco de dados indexado.

Existem algumas restrições com os trabalhadores da web:

  1. Você não pode manipular diretamente o DOM de dentro de um trabalhador.
  2. Você não pode usar alguns métodos e propriedades padrão do objeto de janela, pois o objeto de janela não está disponível dentro de um thread de trabalho.
  3. O contexto dentro do segmento de trabalho pode ser acessado via DedicatedWorkerGlobalScope ou SharedWorkerGlobalScope dependendo do uso.

Recursos dos Trabalhadores da Web

Existem dois tipos de web workers:

  1. Web worker dedicado – Um worker dedicado só pode ser acessado pelo script que o chamou.
  2. Web worker compartilhado – Um worker compartilhado pode ser acessado por vários scripts – mesmo que estejam sendo acessados por diferentes janelas, iframes ou até workers.

Vamos discutir mais sobre esses dois tipos de trabalhadores da web.

Criação de um trabalhador da web

A criação é praticamente a mesma para um trabalhador da Web dedicado e compartilhado.

Criar um novo funcionário dedicado é simples: basta chamar o construtor Worker e passar o caminho do script que você deseja executar como o worker.

main.js

Criar um novo trabalhador compartilhado é praticamente o mesmo que o de um trabalhador dedicado, mas com um nome de construtor diferente:

shared-main.js

Comunicação entre main e thread de trabalho

A comunicação entre o encadeamento principal e o encadeamento do trabalhador ocorre por meio do método postMessage e do manipulador de eventos onmessage .

No caso de um web worker dedicado, o sistema de comunicação é simples. Você só precisa usar o método postMessage sempre que quiser enviar uma mensagem para o trabalhador.

main.js

E dentro de um web worker, você pode responder quando a mensagem é recebida escrevendo um bloco manipulador de eventos como este:

main-worker.js

O manipulador onmessage permite que você execute algum código sempre que uma mensagem é recebida.

Aqui estamos calculando a média de números e depois usando postMessage () novamente para postar o resultado de volta ao thread principal. Como você pode ver na linha 6 em main.js, usamos o evento onmessage na instância do trabalhador. Portanto, sempre que o thread de trabalho usa postMessage, onmessage no thread principal é acionado.

No caso de um trabalhador da Web compartilhado, o sistema de comunicação é um pouco diferente. Como um trabalhador é compartilhado entre vários scripts, precisamos nos comunicar por meio do objeto de porta da instância do trabalhador. Isso é feito implicitamente no caso de trabalhadores dedicados. Você precisa usar o método postMessage sempre que quiser enviar uma mensagem para o trabalhador.

main-shared.js

Dentro de um trabalhador da web ( main-shared-worker.js ) é um pouco complexo. Primeiro, usamos um manipulador onconnect para disparar código quando ocorre uma conexão com a porta ( linha 2 ). Usamos o atributo ports desse objeto de evento para pegar a porta e armazená-la em uma variável ( linha 4 ). Em seguida, adicionamos um manipulador de mensagens na porta para fazer o cálculo e retornar o resultado para o segmento principal ( linha 7 e linha 25 ) assim:

main-shared-worker.js

Rescisão de um trabalhador da web

Se você precisar encerrar imediatamente um trabalhador em execução do thread principal, poderá fazê-lo chamando o método terminate do worker:

terminar

O thread de trabalho é morto imediatamente sem uma oportunidade para concluir suas operações.

Desova do trabalhador da web

Os trabalhadores podem gerar mais trabalhadores se desejarem. Mas eles devem ser hospedados na mesma origem da página pai.

Importando Scripts

Threads de trabalho têm acesso a uma função global, importScripts (), que permite importar scripts.

importando scripts

Demonstração de Trabalho

Discutimos algumas das abordagens acima para obter uma programação assíncrona para que nossa interface não seja bloqueada devido a qualquer tarefa computacional pesada. Mas existem algumas limitações para essas abordagens. Assim, podemos usar os funcionários da Web para resolver esses problemas de maneira eficiente.

Clique aqui para executar esta demonstração ao vivo.

Aqui, você verá três seções:

  1. Código de Bloqueio : Quando você clica em calcular média , o carregador não exibe e depois de algum tempo você vê o resultado final e o tempo gasto. Isso ocorre porque, assim que o método médio é chamado, eu também ativei o método showLoader . Mas como JS é single threaded, ele não executará showLoader até que a execução da média seja concluída. Então, você não poderá ver o carregador neste caso nunca.
  2. Código Async : Neste eu tentei alcançar a mesma funcionalidade usando o método setTimeout e colocando toda execução de função em um loop de eventos. Você verá o carregador neste caso, mas a resposta leva tempo em comparação com o método definido acima.
  3. Web worker : Este é um exemplo de uso de um trabalhador da web. Neste, você verá o carregador assim que clicar em calcular média e obterá uma resposta no mesmo tempo do método 1, para o mesmo número.

Você pode acessar o código-fonte para o mesmo aqui .

Conceitos avançados

Existem alguns conceitos avançados relacionados aos web workers. Nós não vamos discuti-los em detalhes, mas é bom saber sobre eles.

  1. Política de segurança de conteúdo – Os trabalhadores da Web têm seu próprio contexto de execução, independentemente do documento que os criou. Por causa disso, eles não são regidos pela Política de Segurança de Conteúdo do thread / worker pai. A exceção a isso é se a origem do script do trabalhador for um identificador global exclusivo (por exemplo, se a URL tiver um esquema de dados ou blob). Nesse caso, o trabalhador herda a diretiva de segurança de conteúdo do documento ou do trabalhador que a criou.
  2. Transferindo dados de e para trabalhadores – Os dados transmitidos entre o thread principal e o de trabalho são copiados e não compartilhados. Os objetos são serializados conforme são entregues ao trabalhador e, subsequentemente, são desserializados na outra extremidade. A página e o worker não compartilham a mesma instância , portanto, o resultado final é que uma duplicata é criada em cada extremidade. Os navegadores implementaram o algoritmo de clonagem estruturada para conseguir isso.
  3. Trabalhadores incorporados – Você também pode incorporar o código do trabalhador dentro de uma página da Web (html). Para isso, você precisa adicionar uma tag de script sem um atributo src e atribuir um tipo MIME não executável a ela, assim:

incorporar trabalhador em html

Existem muitos casos de uso para usar web workers em nosso aplicativo. Eu acabei de discutir um pequeno cenário. Espero que isso ajude você a entender o conceito de trabalhadores da web.

[Links]

Github Repo: https://github.com/bhushangoel/webworker-demo-1 Web trabalhador em ação: https://bhushangoel.github.io/webworker-demo-1/ JS demo showcase: https: //bhushangoel.github .io /

Obrigado por ler.

Feliz Aprendizagem 🙂