(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)
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
})
})
})
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:
- 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
- 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
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.
-
nao use
.then
-
se a função que vc está escrevendo vai usar algo assíncrono, comece ela com
async
-
sempre que fizer uma chamada assincrona numa função
async
, coloqueawait
antes -
bonus: A. use
try/catch
. B. se suas chamadas async puderem ser feitas em paralelo, usePromise.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
-
promises são apenas monads de computações futuras,
.then
é>>=
, easync\await
édo {}