Skip to content

Instantly share code, notes, and snippets.

@VictorTaelin
Last active April 23, 2022 15:18
Show Gist options
  • Star 16 You must be signed in to star a gist
  • Fork 4 You must be signed in to fork a gist
  • Save VictorTaelin/a3271d0939be15ea95e65b171c13f224 to your computer and use it in GitHub Desktop.
Save VictorTaelin/a3271d0939be15ea95e65b171c13f224 to your computer and use it in GitHub Desktop.
promises de novo

(edit: clique aqui para um fork desse texto melhor editado, feito pelo Kewerson Hugo; não vou mexer no original pq deu resultado, e eu gostaria de observar se isso pode ser parcialmente atribuido ao seu estilo)

o pq de funcões assíncronas

funcoes de javascript quase sempre são assincronas. isso acontece porque js lida muito com coisas como http requests, leitura de disco... operacoes que levam muito tempo. imagina se seu programa travasse toda vez que vc fizesse um request http? pra evitar isso, o request é feito em background, o programa continua rodando, e depois o resultado é enviado pra vc num callback

request("www.google.com", function callback(resultado) { ... })

isso é bom pra performance, mas gera codigo dificil de ler, porque vira uma arvore de identação:

request("blabla1", function callback(blabla1) {
  request("blabla2", function callback(blabla2) {
    request("blabla3", function callback(blabla3) {
      console.log(blabla3); // imprime o ultimo blabla
    })
  })
})

promises: simplificando funcões assíncronas

eventualmente inventaram promises, que são "valores que serão resolvidos futuramente". eles podem ser passados por aí como valores normais de JS, e vc pode usar .then(...) para "esperar" o resultado. além do que, eles podem ser encadeados. cada .then tem acesso ao valor retornado pelo .then anterior. por fim, vc pode capturar todos os erros no final da cadeia, com um .catch.

request("blabla1")
  .then(blabla1 => request("blabla2"))
  .then(blabla2 => request("blabla3"))
  .then(blabla3 => console.log(blabla3)) // imprime o ultimo blabla
  .catch(error => ...) // uma vez so, captura todos os erros acima!

esse estilo permite simplificar muito códigos assíncronos, mas ainda é meio bosta. imagina que vc precisa de todos os blablas na ultima linha?

request("blabla1")
  .then(blabla1 => request("blabla2"))
  .then(blabla2 => request("blabla3"))
  .then(blabla3 => console.log(blabla1, blabla2, blabla3)) // imprime todos os blablas

pára um tempo pra ler o codigo acima e perceba o erro. blabla1 e blabla2 não estão em escopo no ultimo then! tem 2 jeitos de resolver isso:

  1. passa os blablas adiante, até chegar no ultimo then:
request("blabla1")
  .then(blabla1 => Promise.all([blabla1, request("blabla2")]))
  .then(([blabla1, blabla2]) => Promise.all([blabla1, blabla2, request("blabla3")]))
  .then(([blabla1, blabla2, blabla3]) => console.log(blabla1, blabla2, blabla3)) // imprime todos os blablas
  1. identa para a direita:
request("blabla1")
  .then(blabla1 => request("blabla2")
    .then(blabla2 => request("blabla3")
      .then(blabla3 => console.log(blabla1, blabla2, blabla3)) // imprime todos os blablas
    )
  );

como vc pode ver, o primeiro é uma bosta de ler, e o segundo traz o mesmo problema dos callbacks. a solução definitiva disso é o async/await.

PS: nesse caso vc poderia usar Promise.all nos 3 requests logo no início, mas não daria pra fazer isso se os requests dependessem dos valores dos blablas intermediários. o ponto é: .then não resolve todos os casos

async: simplificando promises

asyncs funcionam junto com promises. basicamente, toda função que tem async retorna um promise. por ex:

const add = (a, b) => new Promise((resolve, reject) => resolve(a + b));

add(1, 2).then(result => console.log(result)); // imprime 3

isso pode ser reescrito como:

const add = async (a, b) => a + b;

add(1, 2).then(result => console.log(result)); // imprime 3

repara que async é só um shortcut para escrever uma função que retorna um promise. mas o legal é que, dentro de uma função async, vc pode usar await promise, que pára a execução da função até que o promise resolva. repare que isso não é um problema, pois, como a função é assincrona, isso não vai "travar o seu programa" (o que aconteceria se o await pudesse ser usado em funções normais!). só vai travar a própria função, que roda em background, então tanto faz. assim, com o async/await, você consegue escrever funções assíncronas com a "mesma cara" de funções normais.

(async () => {

  var blabla1 = await request("blabla1");
  var blabla2 = await request("blabla2");
  var blabla3 = await request("blabla3");

  console.log(blabla1, blabla2, blabla3);
  
})();

esse código é equivalente a esse:

request("blabla1")
  .then(blabla1 => request("blabla2")
    .then(blabla2 => request("blabla3")
      .then(blabla3 => console.log(blabla1, blabla2, blabla3)) // imprime todos os blablas
    )
  );

o compilador adiciona as identações automaticamente, e o código fica muito mais normal e legível.

resumo

  1. nao use .then

  2. se a função que vc está escrevendo vai usar algo assíncrono, comece ela com async

  3. sempre que fizer uma chamada assincrona numa função async, coloque await antes

  4. bonus: A. use try/catch. B. se suas chamadas async puderem ser feitas em paralelo, use Promise.all. ex:

    (async () => {
    
      try {
    
        // não vamos usar await ainda
        var blabla1_p = request("blabla1");
        var blabla2_p = request("blabla2");
        var blabla3_p = request("blabla3");
       
        // combine os promises em um único promise em paralelo
        var allBlablas_p = Promise.all([blabla1_p, blabla2_p, blabla3_p]);
       
        // aí sim use await
        var [blabla1, blabla2, blabla3] = await allBlablas_p;
       
        console.log(blabla1, blabla2, blabla3);
    
      } catch (error) {
        // seja educado e trate os erros corretamente
      }
      
    })();

    esse último código seria a melhor versão dessa nossa função

  5. promises são apenas monads de computações futuras, .then é >>=, e async\await é do {}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment