Como escrever funções assíncronas para Node.js

114

Eu tentei pesquisar sobre como exatamente as funções assíncronas devem ser escritas. Após vasculhar bastante documentação, ainda não está claro para mim.

Como escrevo funções assíncronas para o Node? Como devo implementar o tratamento de eventos de erro corretamente?

Outra maneira de fazer minha pergunta seria esta: Como devo interpretar a seguinte função?

var async_function = function(val, callback){
    process.nextTick(function(){
        callback(val);
    });
};

Além disso, achei interessante esta questão no SO ("Como faço para criar uma função assíncrona sem bloqueio em node.js?"). Acho que ainda não foi respondido.

Kriem
fonte
14
É por isso que estou perguntando. Não está claro para mim como essas funções são diferentes.
Kriem
Eu recomendo que você olhar setTimeoute setIntervalno seu navegador favorito e brincar com eles também. Ou callbacks ajax (provavelmente o mais próximo da experiência do nó), ou ouvintes de eventos para coisas com as quais você está familiarizado, como eventos de clique e carregamento. O modelo assíncrono já existe no navegador e eles são exatamente os mesmos no nó.
davin
@davin - Acho que não compreendo totalmente o modelo assíncrono então.
Kriem
@Kriem, eu respondi uma coisa ontem que pode ajudar: stackoverflow.com/questions/6883648/… Não é uma resposta à sua pergunta, mas é no tópico. Tente ler a pergunta e a resposta e experimente o código para tentar entender o que está acontecendo.
davin
2
@Raynos Qual é a definição de "função assíncrona"?
Anderson Green,

Respostas:

85

Você parece estar confundindo IO assíncrona com funções assíncronas. node.js usa IO sem bloqueio assíncrono porque IO sem bloqueio é melhor. A melhor maneira de entender isso é assistir a alguns vídeos de ryan dahl.

Como escrevo funções assíncronas para o Node?

Basta escrever funções normais, a única diferença é que elas não são executadas imediatamente, mas transmitidas como retornos de chamada.

Como devo implementar o tratamento de eventos de erro corretamente

Geralmente APIs fornecem um retorno de chamada com um erro como o primeiro argumento. Por exemplo

database.query('something', function(err, result) {
  if (err) handle(err);
  doSomething(result);
});

É um padrão comum.

Outro padrão comum é on('error'). Por exemplo

process.on('uncaughtException', function (err) {
  console.log('Caught exception: ' + err);
});

Editar:

var async_function = function(val, callback){
    process.nextTick(function(){
        callback(val);
    });
};

A função acima quando chamada como

async_function(42, function(val) {
  console.log(val)
});
console.log(43);

Será impresso 42no console de forma assíncrona. Em particular, process.nextTickdispara depois que a pilha de chamadas do eventloop atual está vazia. Essa pilha de chamadas está vazio depois async_functione console.log(43)ter executado. Então, imprimimos 43 seguido de 42.

Você provavelmente deve fazer alguma leitura sobre o loop de eventos.

Raynos
fonte
Eu vi os vídeos de Dahl, mas acho que não consigo entender o assunto. :(
Kriem
1
@Kriem veja a resposta atualizada e leia sobre o loop de eventos
Raynos
1
Obrigado pelos insights. Agora estou mais ciente do que me falta em conhecimento. :) Seu último exemplo ajudou a propósito.
Kriem
Acho que sua afirmação de que IO assíncrono é "melhor" é muito geral. Nesse sentido, sim, mas no geral pode não ser o caso.
Jake B
Em seu primeiro exemplo de código, você verifica o argumento err, mas não retorna depois. No caso de um erro, o código continuará e potencialmente causará sérios problemas em seu aplicativo.
Gabriel McAdams
9

Apenas passar por callbacks não é suficiente. Você tem que usar settimer, por exemplo, para tornar a função assíncrona.

Exemplos: funções não assíncronas:

function a() {
  var a = 0;    
  for(i=0; i<10000000; i++) {
    a++;
  };
  b();
};

function b() {
  var a = 0;    
  for(i=0; i<10000000; i++) {
    a++;
  };    
  c();
};

function c() {
  for(i=0; i<10000000; i++) {
  };
  console.log("async finished!");
};

a();
console.log("This should be good");

Se você executar o exemplo acima, isso deve ser bom, terá que esperar até que essas funções terminem de funcionar.

Funções pseudo multithread (assíncronas):

function a() {
  setTimeout ( function() {
    var a = 0;  
    for(i=0; i<10000000; i++) {
      a++;
    };
    b();
  }, 0);
};

function b() {
  setTimeout ( function() {
    var a = 0;  
    for(i=0; i<10000000; i++) {
      a++;
    };  
    c();
  }, 0);
};

function c() {
  setTimeout ( function() {
    for(i=0; i<10000000; i++) {
    };
    console.log("async finished!");
  }, 0);
};

a();
console.log("This should be good");

Este será verdadeiramente assíncrono. Isso deve ser bom e será escrito antes de terminar o assíncrono.

Calmbird
fonte
3

Se você SABE que uma função retorna uma promessa, sugiro usar os novos recursos async / await em JavaScript. Faz com que a sintaxe pareça síncrona, mas funcione de forma assíncrona. Quando você adiciona a asyncpalavra-chave a uma função, isso permite que você faça awaitpromessas nesse escopo:

async function ace() {
  var r = await new Promise((resolve, reject) => {
    resolve(true)
  });

  console.log(r); // true
}

se uma função não retornar uma promessa, recomendo envolvê-la em uma nova promessa definida por você e, em seguida, resolva os dados que deseja:

function ajax_call(url, method) {
  return new Promise((resolve, reject) => {
    fetch(url, { method })
    .then(resp => resp.json())
    .then(json => { resolve(json); })
  });
}

async function your_function() {
  var json = await ajax_call('www.api-example.com/some_data', 'GET');
  console.log(json); // { status: 200, data: ... }
}

Resultado: aproveite o poder das Promessas.

ryanwaite28
fonte
Uma coisa a lembrar aqui é que o corpo da promessa ainda é executado de forma síncrona.
shadow0359
2

Tente fazer isso, funciona tanto para o nó quanto para o navegador.

isNode = (typeof exports !== 'undefined') &&
(typeof module !== 'undefined') &&
(typeof module.exports !== 'undefined') &&
(typeof navigator === 'undefined' || typeof navigator.appName === 'undefined') ? true : false,
asyncIt = (isNode ? function (func) {
  process.nextTick(function () {
    func();
  });
} : function (func) {
  setTimeout(func, 5);
});
Pradeep
fonte
18
4 votos negativos e nenhum comentário construtivo ..: \
Omer
6
@Omer Assim é a vida no SO.
Peça Digital
6
@NorbertoBezi Talvez o código seja autoexplicativo para você, mas não para quem postou a resposta. É por isso que é sempre uma boa prática explicar sobre uma votação negativa.
Omer
0

Estou lidando com muitas horas para essa tarefa em node.js. Eu sou principalmente o cara do front-end.

Acho isso muito importante, porque todos os métodos de nó assíncronos lidam com callback, e transformá-lo em Promise é melhor lidar com isso.

Eu só quero mostrar um resultado possível, mais enxuto e legível. Usando ECMA-6 com assíncrono, você pode escrever assim.

 async function getNameFiles (dirname) {
  return new Promise((resolve, reject) => {
    fs.readdir(dirname, (err, filenames) => {
      err !== (undefined || null) ? reject(err) : resolve(filenames)
    })
  })
}

o (undefined || null)é para repl cenários (Leia evento de loop de impressão), usando o trabalho indefinido também.

Yoarthur
fonte