Quero criar uma página que exiba alguns dados de um banco de dados, por isso criei algumas funções que obtêm esses dados do meu banco de dados. Sou apenas um novato no Node.js, pelo que entendi, se eu quiser usá-los em uma única página (resposta HTTP), precisarei aninhar todos:
http.createServer(function (req, res) {
res.writeHead(200, {'Content-Type': 'text/html'});
var html = "<h1>Demo page</h1>";
getSomeDate(client, function(someData) {
html += "<p>"+ someData +"</p>";
getSomeOtherDate(client, function(someOtherData) {
html += "<p>"+ someOtherData +"</p>";
getMoreData(client, function(moreData) {
html += "<p>"+ moreData +"</p>";
res.write(html);
res.end();
});
});
});
Se houver muitas funções como essa, o aninhamento se tornará um problema .
Há alguma maneira de evitar isto? Eu acho que tem a ver com a maneira como você combina várias funções assíncronas, o que parece ser algo fundamental.
getSomeDate
egetSomeOtherDate
acaba alterando o recuo de muitas linhas, o que dificulta a leitura do histórico do git (git blame
é inútil depois disso), e você provavelmente comete erros ao fazer isso manualmenteRespostas:
Observação interessante. Observe que, em JavaScript, você normalmente pode substituir funções de retorno de chamada anônimas inline por variáveis de função nomeadas.
Os seguintes:
Pode ser reescrito para ter algo parecido com isto:
No entanto, a menos que você planeje reutilizar a lógica de retorno de chamada em outros lugares, geralmente é muito mais fácil ler funções anônimas inline, como no seu exemplo. Isso também poupará você de encontrar um nome para todos os retornos de chamada.
Além disso, observe que, como o @pst observou em um comentário abaixo, se você estiver acessando variáveis de fechamento dentro das funções internas, o acima não seria uma tradução direta. Nesses casos, o uso de funções anônimas inline é ainda mais preferível.
fonte
getMoreData
é perdido.someDataParser
na verdade, analisa TODOS os dados, pois também chamagetMoreData
. Nesse sentido, o nome da função está incorreto e torna-se aparente que realmente não removemos o problema de aninhamento.Kay, basta usar um desses módulos.
Vai virar isso:
Nisso:
fonte
Na maior parte, eu concordo com Daniel Vassallo. Se você pode dividir uma função complicada e profundamente aninhada em funções nomeadas separadas, geralmente é uma boa idéia. Nos momentos em que faz sentido fazê-lo em uma única função, você pode usar uma das muitas bibliotecas assíncronas node.js disponíveis. As pessoas criaram várias maneiras diferentes de resolver isso, então dê uma olhada na página de módulos node.js. e veja o que você pensa.
Eu mesmo escrevi um módulo para isso, chamado async.js . Usando isso, o exemplo acima pode ser atualizado para:
Uma coisa legal dessa abordagem é que você pode alterar rapidamente seu código para buscar os dados em paralelo, alterando a função 'series' para 'paralelamente'. Além disso, o async.js também funcionará dentro do navegador, para que você possa usar os mesmos métodos usados no node.js, caso encontre algum código assíncrono complicado.
Espero que seja útil!
fonte
Você pode usar esse truque com uma matriz em vez de funções aninhadas ou um módulo.
Muito mais fácil para os olhos.
Você pode estender o idioma para processos paralelos ou mesmo cadeias paralelas de processos:
fonte
Eu gosto muito de async.js para esse fim.
O problema foi resolvido pelo comando waterfall:
Exemplo
Quanto às variáveis req, res, elas serão compartilhadas no mesmo escopo da função (req, res) {}, que encerrou toda a chamada async.waterfall.
Além disso, o assíncrono é muito limpo. O que quero dizer é que eu mudo muitos casos como este:
Para primeiro:
Então para isso:
Então para isso:
Ele também permite que muitas funções pré-fabricadas preparadas para o assíncrono sejam chamadas a partir do util.js muito rapidamente. Apenas encadeie o que você quer fazer, certifique-se de que o, cb seja tratado universalmente. Isso acelera bastante todo o processo de codificação.
fonte
O que você precisa é de um pouco de açúcar sintático. Chek isto:
Bem arrumado , não é? Você pode perceber que o html se tornou uma matriz. Isso ocorre em parte porque as strings são imutáveis, então é melhor você armazenar sua saída em buffer em uma matriz do que descartar strings cada vez maiores. A outra razão é por causa de outra sintaxe agradável com
bind
.Queue
no exemplo é realmente apenas um exemplo e, juntamente com,partial
pode ser implementado da seguinte maneirafonte
last
função)obj.email
e sua próxima função usaobj.email
e a exclui (ou apenas atribuinull
).Estou apaixonado pelo Async.js desde que o encontrei. Tem uma
async.series
função que você pode usar para evitar aninhamentos longos.Documentação:-
séries (tarefas, [retorno de chamada])
Execute uma matriz de funções em série, cada uma executando uma vez que a função anterior foi concluída. [...]
Argumentos
tasks
- Uma matriz de funções a serem executadas; cada função recebe um retorno de chamada que deve chamar ao concluir.callback(err, [results])
- Um retorno de chamada opcional para executar quando todas as funções forem concluídas. Essa função obtém uma matriz de todos os argumentos passados para os retornos de chamada usados na matriz.Veja como podemos aplicá-lo ao seu código de exemplo: -
fonte
O açúcar sintático mais simples que eu já vi é a promessa do nó.
npm install node-promessa || git clone https://github.com/kriszyp/node-promise
Usando isso, você pode encadear métodos assíncronos como:
O valor de retorno de cada um está disponível como argumento no próximo.
fonte
O que você fez lá é pegar um padrão assíncrono e aplicá-lo a 3 funções chamadas em sequência, cada uma aguardando a conclusão da anterior antes de iniciar - ou seja, você as tornou síncronas . O ponto sobre a programação assíncrona é que você pode ter várias funções em execução ao mesmo tempo e não ter que esperar a conclusão de cada uma.
se getSomeDate () não fornece nada para getSomeOtherDate (), que não fornece nada para getMoreData (), por que você não os chama de forma assíncrona como js permite ou se eles são interdependentes (e não assíncronos) os escrevem como função única?
Você não precisa usar o aninhamento para controlar o fluxo - por exemplo, faça com que cada função termine chamando uma função comum que determina quando todos os 3 foram concluídos e depois envia a resposta.
fonte
Suponha que você possa fazer isso:
Você só precisa implementar chain () para aplicar parcialmente cada função à próxima e chamar imediatamente apenas a primeira função:
fonte
o inferno de retorno de chamada pode ser facilmente evitado em javascript puro com fechamento. a solução abaixo assume que todos os retornos de chamada seguem a assinatura da função (erro, dados).
fonte
Recentemente, criei uma abstração mais simples chamada wait.for para chamar funções assíncronas no modo de sincronização (com base em fibras). Está em um estágio inicial, mas funciona. Está em:
https://github.com/luciotato/waitfor
Usando wait.for , você pode chamar qualquer função nodejs assíncrona padrão, como se fosse uma função de sincronização.
usando wait.for seu código pode ser:
... ou se você quiser ser menos detalhado (e também adicionar captura de erro)
Em todos os casos, getSomeDate , getSomeOtherDate e getMoreData devem ser funções assíncronas padrão com o último parâmetro, um retorno de chamada de função (err, data)
como em:
fonte
Para resolver esse problema, escrevi nodent ( https://npmjs.org/package/nodent ) que pré-processa invisivelmente seu JS. Seu código de exemplo se tornaria (assíncrono, realmente - leia os documentos).
Claramente, existem muitas outras soluções, mas o pré-processamento tem a vantagem de ter pouca ou nenhuma sobrecarga de tempo de execução e, graças ao suporte ao mapa de origem, é fácil depurar também.
fonte
Eu tive o mesmo problema. Eu vi as principais bibliotecas para executar funções assíncronas em nó, e elas apresentam um encadeamento tão natural (você precisa usar três ou mais métodos confs, etc.) para criar seu código.
Passei algumas semanas desenvolvendo uma solução para ser simples e fácil de ler. Por favor, tente o EnqJS . Todas as opiniões serão apreciadas.
Ao invés de:
com EnqJS:
Observe que o código parece ser maior que antes. Mas não está aninhado como antes. Para parecer mais natural, as cadeias são chamadas imediatamente:
E para dizer que retornou, dentro da função que chamamos:
fonte
Eu faço isso de uma maneira bastante primitiva, mas eficaz. Por exemplo, preciso obter um modelo com seus pais e filhos e digamos que preciso fazer consultas separadas para eles:
fonte
Use Fibers https://github.com/laverdet/node-fibers para que o código assíncrono pareça síncrono (sem bloquear)
Eu pessoalmente uso esse pequeno invólucro http://alexeypetrushin.github.com/synchronize Exemplo de código do meu projeto (todo método é realmente assíncrono, trabalhando com E / S de arquivo assíncrono) Eu até tenho medo de imaginar que confusão seria com retorno de chamada ou bibliotecas auxiliares de controle de fluxo assíncrono.
fonte
Task.js oferece a você o seguinte:
Em vez disso:
fonte
Depois que os outros responderam, você declarou que seu problema eram variáveis locais. Parece que uma maneira fácil de fazer isso é escrever uma função externa para conter essas variáveis locais, depois usar várias funções internas nomeadas e acessá-las pelo nome. Dessa forma, você apenas aninhará duas profundidades, independentemente de quantas funções você precise encadear.
Aqui está a tentativa do meu novato de usar o
mysql
módulo Node.js com aninhamento:A seguir, é reescrita usando funções internas nomeadas. A função externa também
with_connection
pode ser usada como suporte para variáveis locais. (Aqui, eu tenho os parâmetrossql
,bindings
,cb
que atuam de forma semelhante, mas você pode apenas definir algumas variáveis locais adicionaiswith_connection
.)Eu pensava que talvez fosse possível criar um objeto com variáveis de instância e usá-las como um substituto para as variáveis locais. Mas agora acho que a abordagem acima, usando funções aninhadas e variáveis locais, é mais simples e fácil de entender. Demora algum tempo para desaprender OO, parece :-)
Então, aqui está minha versão anterior com um objeto e variáveis de instância.
Acontece que isso
bind
pode ser usado com alguma vantagem. Isso me permite livrar-me das funções anônimas um tanto feias que criei que não fizeram muito, exceto encaminhar-se para uma chamada de método. Não pude passar o método diretamente porque ele estaria envolvido com o valor errado dethis
. Mas combind
, eu posso especificar o valorthis
que eu quero.Obviamente, nada disso é JS adequado com a codificação Node.js. - passei algumas horas nele. Mas talvez com um pouco de polimento essa técnica possa ajudar?
fonte
o async.js funciona bem para isso. Encontrei este artigo muito útil que explica a necessidade e o uso do async.js com exemplos: http://www.sebastianseilund.com/nodejs-async-in-practice
fonte
Se você não quiser usar "step" ou "seq", tente "line", que é uma função simples para reduzir o retorno de chamada assíncrona aninhada.
https://github.com/kevin0571/node-line
fonte
Assíncrona em c #-like é outra maneira de fazer isso
https://github.com/yortus/asyncawait
fonte
Usando wire, seu código ficaria assim:
fonte
para seu conhecimento, considere Jazz.js https://github.com/Javanile/Jazz.js/wiki/Script-showcase
fonte