Eu sei que a function de retorno de chamada é executada de forma assíncrona, mas por quê?

Qual parte da syntax fornece as informações que esta function deve executar em outro thread e ser non-blocking?

Vamos considerar a E / S assíncrona simples em node.js

var fs = require('fs'); var path = process.argv[2]; fs.readFile(path, 'utf8', function(err,data) { var lines = data.split('\n'); console.log(lines.length-1); }); 

O que exatamente faz o truque que acontece em segundo plano? Alguém poderia explicar isso com precisão ou colar um link para algum bom recurso? Onde quer que eu olhe, há muitas informações sobre o que é callback, mas ninguém explica por que ele realmente funciona assim.

Esta não é a questão específica sobre node.js, é sobre o conceito geral de retorno de chamada em cada linguagem de programação.

EDITAR:

Provavelmente, o exemplo que forneci não é o melhor aqui. Então, não considere este snippet de código node.js. Eu estou perguntando geralmente – o que faz o truque que programa continua executando quando encontrar function de retorno de chamada. O que está na syntax que torna o conceito de retorno de chamada não-bloqueador?

Desde já, obrigado!

   

Não há nada na syntax que diga que seu retorno de chamada é executado de forma assíncrona. As chamadas de retorno podem ser assíncronas, como:

 setTimeout(function(){ console.log("this is async"); }, 100); 

ou pode ser síncrono, como:

 an_array.forEach(function(x){ console.log("this is sync"); }); 

Então, como você pode saber se uma function invocará o retorno de chamada de forma síncrona ou assíncrona? A única maneira confiável é ler a documentação.

Você também pode escrever um teste para descobrir se a documentação não está disponível:

 var t = "this is async"; some_function(function(){ t = "this is sync"; }); console.log(t); 

Como funciona o código asynchronous

O Javascript, por si só, não possui nenhum recurso para tornar as funções assíncronas. Se você quiser escrever uma function assíncrona, você tem duas opções:

  1. Use outra function assíncrona, como setTimeout ou web workers, para executar sua lógica.

  2. Escreva em C.

Quanto à forma como as funções codificadas em C (como setTimeout ) implementam a execução assíncrona? Tudo tem a ver com o loop de events (ou principalmente).

O loop de events

Dentro do navegador da web, existe esse código que é usado para redes. Originalmente, o código de rede só podia baixar uma coisa: a própria página HTML. Quando a Mosiac inventou a tag o código de rede evoluiu para baixar vários resources. Então o Netscape implementou a renderização progressiva de imagens, eles tinham que tornar o código de rede asynchronous para que eles pudessem desenhar a página antes de todas as imagens serem carregadas e atualizar cada imagem de forma progressiva e individual. Esta é a origem do loop de events.

No coração do navegador, há um loop de events que evoluiu a partir do código de rede assíncrona. Portanto, não é surpreendente que ele use uma primitiva de E / S como seu núcleo: select() (ou algo similar como poll, epoll etc. dependendo do sistema operacional).

A function select() em C permite que você aguarde várias operações de E / S em um único encadeamento sem precisar gerar encadeamentos adicionais. select() é algo como:

 select (max, readlist, writelist, errlist, timeout) 

Para que ele espere por uma E / S (de um soquete ou disco), você adicionaria o descritor de arquivo à lista de readlist e ele retornará quando houver dados disponíveis em qualquer um dos seus canais de E / S. Depois que ele retornar, você poderá continuar processando os dados.

O interpretador javascript salva seu retorno de chamada e, em seguida, chama a function select() . Quando select() retorna, o interpretador descobre qual retorno de chamada está associado a qual canal de E / S e, em seguida, o chama.

Convenientemente, select() também permite que você especifique um valor de timeout . Ao gerenciar cuidadosamente o timeout passado para select() você pode chamar callbacks em algum momento no futuro. É assim que setTimeout e setInterval são implementados. O intérprete mantém uma lista de todos os tempos limite e calcula o que precisa passar como timeout para select() . Então, quando select() retorna além de descobrir se há algum retorno de chamada que precise ser chamado devido a uma operação de E / S, o interpretador também verifica se há algum timeout expirado que precise ser chamado.

So select() sozinho cobre quase todas as funcionalidades necessárias para implementar funções assíncronas. Mas os navegadores modernos também têm trabalhadores da web. No caso dos web workers, o navegador gera threads para executar o código javascript de forma assíncrona. Para se comunicar de volta ao thread principal, os funcionários ainda devem interagir com o loop de events (a function select() ).

O Node.js também gera threads ao lidar com E / S de arquivo / disco. Quando a operação de E / S é concluída, ela se comunica com o loop de events principal para executar as chamadas de retorno apropriadas.


Espero que isso responda sua pergunta. Eu sempre quis escrever esta resposta, mas estava ocupado para fazer isso anteriormente. Se você quiser saber mais sobre a programação de E / S sem bloqueio no CI, sugira que leia o seguinte: http://www.gnu.org/software/libc/manual/html_node/Waiting-for-I_002fO.html

Um retorno de chamada não é necessariamente asynchronous. A execução depende inteiramente de como o fs.readFile decide tratar o parâmetro da function.

Em JavaScript, você pode executar uma function de forma assíncrona usando, por exemplo, setTimeout .

Discussão e resources:

Como o node.js implementa a E / S sem bloqueio?

Modelo de concurrency e Loop de Eventos

Wikipédia :

Há dois tipos de retorno de chamada, diferindo em como eles controlam o stream de dados no tempo de execução: bloqueando retornos de chamada (também conhecidos como retornos de chamada síncronos ou apenas retornos de chamada) e retornos de chamada adiados (também conhecidos como retornos de chamada asynchronouss).

Primeiro de tudo, se algo não é asynchronous, significa que está bloqueando. Portanto, o gerenciador de javascript pára nessa linha até que essa function termine (é o que um readFileSync faria).

Como todos sabemos, fs é uma biblioteca de IO, então esse tipo de coisa leva tempo (dizer ao hardware para ler alguns arquivos não é algo feito imediatamente), então faz muito sentido que qualquer coisa que não requeira apenas a CPU , é asynchronous, porque leva tempo e não precisa congelar o resto do código para esperar outra peça de hardware (enquanto a CPU estiver inativa).

Espero que isso resolva suas dúvidas.