O JavaScript evoluiu em muito pouco tempo de callbacks para promises (ES2015) e, desde o ES2017, o JavaScript assíncrono é ainda mais simples com a sintaxe async/await.
As funções assíncronas são uma combinação de promises e generators e, basicamente, são uma abstração de nível superior sobre promises. Deixe-me repetir: async/await é baseado em promises.
Eles reduzem o clichê em torno de promises e a limitação "não quebre a cadeia" das promises de encadeamento.
Quando as promises foram introduzidas no ES2015, elas foram feitas para resolver um problema com código assíncrono, e resolveram, mas ao longo dos 2 anos que separam o ES2015 e o ES2017, ficou claro que as promises não poderiam ser a solução definitiva.
Promesas foram introduzidas para resolver o famoso problema do inferno de callbacks, mas elas introduziram complexidade por conta própria e complexidade de sintaxe.
Eles foram bons primitivos em torno dos quais uma sintaxe melhor poderia ser exposta aos desenvolvedores, então, quando chegasse a hora certa, obtivemos funções assíncronas.
Eles fazem o código parecer assíncrono, mas é assíncrono e não bloqueante nos bastidores.
Uma função assíncrona retorna uma promise, como nesse exemplo:
const doSomethingAsync = () => {
return new Promise(resolve => {
setTimeout(() => resolve('I did something'), 3000);
});
};
Quando você deseja chamar esta função, você adiciona await
e o código de chamada parará até que a promise seja resolvia ou rejeitada. Uma ressalva: a função do cliente deve ser definida como async
(assíncrona). Aqui está um exemplo:
const doSomething = async () => {
console.log(await doSomethingAsync());
};
Esse simples exemplo de async/await usado para executar uma função de forma assíncrona:
const doSomethingAsync = () => {
return new Promise(resolve => {
setTimeout(() => resolve('I did something'), 3000)
})
}
const doSomething = async () => {
console.log(await doSomethingAsync())
}
console.log('Before')
doSomething()
console.log('After')
Anexar a palavra-chave async
a qualquer função significa que a função retornará uma promise.
Mesmo que não esteja fazendo isso explicitamente, internamente fará com que retorne uma promise.
É por isso que este código é válido:
const aFunction = async () => {
return 'test';
};
aFunction().then(alert); // This will alert 'test'
é o mesmo que:
const aFunction = () => {
return Promise.resolve('test');
};
aFunction().then(alert); // This will alert 'test'
Como você pode ver no exemplo acima, nosso código parece muito simples. Compare-o com o código usando promises simples, com funções de encadeamento e callback.
E este é um exemplo muito simples, os maiores benefícios surgirão quando o código for muito mais complexo.
Por exemplo, veja como você obteria um recurso JSON e o analisaria usando promises:
const getFirstUserData = () => {
return fetch('/users.json') // get users list
.then(response => response.json()) // parse JSON
.then(users => users[0]) // pick first user
.then(user => fetch(`/users/${user.name}`)) // get user data
.then(userResponse => userResponse.json()); // parse JSON
};
getFirstUserData();
e aqui está a mesma funcionalidade só que usando await/async:
const getFirstUserData = async () => {
const response = await fetch('/users.json'); // get users list
const users = await response.json(); // parse JSON
const user = users[0]; // pick first user
const userResponse = await fetch(`/users/${user.name}`); // get user data
const userData = await userResponse.json(); // parse JSON
return userData;
};
getFirstUserData();
As funções assíncronas podem ser encadeadas com muita facilidade e a sintaxe é muito mais legível do que com promises simples:
const promiseToDoSomething = () => {
return new Promise(resolve => {
setTimeout(() => resolve('I did something'), 10000)
})
}
const watchOverSomeoneDoingSomething = async () => {
const something = await promiseToDoSomething()
return something + '\nand I watched'
}
const watchOverSomeoneWatchingSomeoneDoingSomething = async () => {
const something = await watchOverSomeoneDoingSomething()
return something + '\nand I watched as well'
}
watchOverSomeoneWatchingSomeoneDoingSomething().then(res => {
console.log(res)
})
As promises de depuração são difíceis porque depurador não passará por cima do código assíncrono.
Async/await torna isso muito fácil porque para o compilador é como código síncrono.