Assíncrono JS sob o capô
MDN define o fluxo de trabalho assíncrono como
..iniciar uma tarefa potencialmente demorada e ainda ser capaz de responder a outros eventos enquanto essa tarefa é executada... Assim que a tarefa for concluída, o resultado será apresentado ao seu programa.
Mas como exatamente isso é alcançável na web com JavaScript?
Antes de mergulhar nas implementações assíncronas, é útil ter um entendimento básico do JS em um nível inferior.
Como funciona o JS?
O JavaScript é single-threaded e interpretado, o que significa que as instruções são executadas diretamente e não possuem etapa de compilação ex-ante. Tomemos como exemplo o V8, o motor mais popular.
Em um nível alto, o V8 analisa o código, cria uma árvore de sintaxe abstrata (AST) e a compila em bytecode otimizado com o interpretador. Há também um preparador, dois compiladores e armazenamento em cache que o tornam rápido (o V8 tem ótimos blogs sobre isso). Ele nos fornece apenas uma pilha de chamadas, heap e loop de eventos ( mais ou menos, na verdade não) . Podemos ignorar o heap para os propósitos desta toca de coelho: é um pedaço de memória não estruturada com referências a objetos e variáveis.
Ciclo de Eventos de Tempo de Execução
Ao contrário de Java ou C++, JS permite a execução de um evento por vez. Podemos ver isso em nossos navegadores traçando um caminho de chamada com console.trace():
// shoutout to ES6 arrow functions
foo = () => {
console.log("Hello, World!");
bar();
}
bar = () => {
console.log("Goodbye, World!");
trace();
}
trace = () => console.trace();
foo();
Hello, World! VM1049:6
Goodbye, World! VM1049:11
// console.trace
trace @ VM1049:11
bar @ VM1049:7
foo @ VM1049:3
call stack visualization
SetTimeout
Vamos executar os mesmos métodos com um atraso assíncrono de 5000 ms com setTimeout().
foo = () => {
console.log("Hello, World!");
setTimeout(bar, 5000);
}
bar = () => {
console.log("Goodbye, World!");
trace();
}
trace = () => console.trace();
foo();
Hello, World! VM1526:2
// 3 days later...
Goodbye, World! VM1526:6
// console.trace
trace @ VM1382:10
bar @ VM1382:6
// setTimeout is invoked
b @ main-base.bundle.UM3…oFn_e5gQ.12.js:1723
setTimeout (async)
Cga.window.setTimeout @ main-base.bundle.UM3…toFn_e5gQ.12.js:516
// foo is pushed to the stack first
foo @ VM1382:3
setTimeoutnão faz parte do mecanismo JS, o que faz sentido, pois o mecanismo pode manipular um evento por vez. Os navegadores , no entanto, são multiencadeados e nos fornecem APIs da web para acessar as implementações setTimeout. Por exemplo, o Chrome usa o mecanismo V8 de thread único em uma arquitetura de vários processos, onde cada processo é altamente multithread. Nesse caso, isso foi chamado explicitamente com Cga.setTimeout @ main-base.bundle.blah-blah-blah...Virtually, isso nos permite usar vários threads.
O Ciclo de Eventos
Vamos reordenar nosso bloco de código:
foo = () => {
console.log("Hello, World!");
setTimeout(bar, 5000);
}
bar = () => {
setTimeout(() => console.log("Goodbye, World!"), 5000);
trace();
}
trace = () => console.trace();
foo();
// output of foo
Hello, World! VM1649:11
// console.trace
trace @ VM1694:11
bar @ VM1694:8
b @ main-base.bundle.UM3…oFn_e5gQ.12.js:1723
setTimeout (async)
Cga.window.setTimeout @ main-base.bundle.UM3…toFn_e5gQ.12.js:516
foo @ VM1694:3
// end trace
// 3 days later...
VM1649:6 Goodbye, World!
O JRE inclui:
- Motor JS (single-threaded)
- Java Virtual Machine (JVM) — A máquina virtual que executa o código em tempo de execução. Aliás, é por isso que “VM” precede os logs de rastreamento.
- API Web
- fila de tarefas
- Loop de eventos
A pilha de chamadas do nosso mecanismo está exibindo os eventos um por um. Depois de ver setTimeout(), o método, junto com o retorno de chamada (referência de função), é delegado à API da web.
Observe que o mecanismo não se importa mais com o modo como a implementação setTimeout da API lida com o evento e o retorno de chamada. O mecanismo segue para exibir o próximo evento e alimentá-lo para o Pacman (a JVM que executa o código).
Enquanto isso, a API usa seus threads para esperar 5000 ms e, posteriormente, os entrega à fila de tarefas. O loop de eventos vê que o evento entra na fila e o empurra de volta para a pilha de chamadas se e somente se a pilha de chamadas estiver vazia.
Você pode usar o divertido visualizador de Philip Roberts para ver tudo em ação. Além disso, confira sua palestra incrível no JSConf em 2014.





































![O que é uma lista vinculada, afinal? [Parte 1]](https://post.nghiatu.com/assets/images/m/max/724/1*Xokk6XOjWyIGCBujkJsCzQ.jpeg)