Funções assíncronas permitem escrever código baseado em promessas como se fosse síncrono.
As funções assíncronas são ativadas por padrão no Chrome, Edge, Firefox e Safari. eles são realmente maravilhosos. Eles permitem que você escreva códigos baseados em promessas se fosse síncrona, mas sem bloquear a linha de execução principal. Eles tornam o seu o código assíncrono é menos "inteligente" e mais legível.
Funções assíncronas funcionam assim:
async function myFirstAsyncFunction() {
try {
const fulfilledValue = await promise;
} catch (rejectedValue) {
// …
}
}
Se você usar a palavra-chave async
antes de uma definição de função, poderá usar
await
na função. Quando você usa await
em uma promessa, a função é pausada.
sem bloqueio até que a promessa seja resolvida. Se a promessa for atendida,
recuperar o valor. Se a promessa for rejeitada, o valor rejeitado será gerado.
Suporte ao navegador
Exemplo: registrar uma busca
Digamos que você queira buscar um URL e registrar a resposta como texto. Veja como fica: usando promessas:
function logFetch(url) {
return fetch(url)
.then((response) => response.text())
.then((text) => {
console.log(text);
})
.catch((err) => {
console.error('fetch failed', err);
});
}
E aqui está a mesma coisa usando funções assíncronas:
async function logFetch(url) {
try {
const response = await fetch(url);
console.log(await response.text());
} catch (err) {
console.log('fetch failed', err);
}
}
É o mesmo número de linhas, mas todos os callbacks sumiram. Assim, mais fáceis de ler, especialmente para aqueles menos familiarizados com promessas.
Valores de retorno assíncronos
Funções assíncronas sempre retornam uma promessa, quer você use await
ou não. Isso
promessa resolve com qualquer coisa que a função assíncrona retorne, ou é rejeitada com
qualquer coisa que a função assíncrona gere. Então, com:
// wait ms milliseconds
function wait(ms) {
return new Promise((r) => setTimeout(r, ms));
}
async function hello() {
await wait(500);
return 'world';
}
... chamar hello()
retorna uma promessa que se cumpre com "world"
.
async function foo() {
await wait(500);
throw Error('bar');
}
... chamar foo()
retorna uma promessa que é rejeitada com Error('bar')
.
Exemplo: streaming de uma resposta
A vantagem das funções assíncronas aumenta em exemplos mais complexos. Digamos que você queira para transmitir uma resposta ao registrar os blocos e retornar o tamanho final.
Aqui está com promessas:
function getResponseSize(url) {
return fetch(url).then((response) => {
const reader = response.body.getReader();
let total = 0;
return reader.read().then(function processResult(result) {
if (result.done) return total;
const value = result.value;
total += value.length;
console.log('Received chunk', value);
return reader.read().then(processResult);
});
});
}
Confira, Jake, "portador de promessas" Archibald. Veja como estou ligando
processResult()
dentro de si para configurar um loop assíncrono? A escrita que fez
me sentir muito inteligente. Mas como a maioria das "inteligentes" no código, precisamos analisá-lo
para descobrir o que ela está fazendo, como uma daquelas fotos de olho mágico
anos 90.
Vamos tentar novamente com funções assíncronas:
async function getResponseSize(url) {
const response = await fetch(url);
const reader = response.body.getReader();
let result = await reader.read();
let total = 0;
while (!result.done) {
const value = result.value;
total += value.length;
console.log('Received chunk', value);
// get the next result
result = await reader.read();
}
return total;
}
Todos os "inteligentes" desapareceu. O loop assíncrono que me fez sentir tão presunçoso é
substituído por uma repetição confiável e entediante. Muito melhor. No futuro, você terá
iteradores assíncronos,
o que
Substitua a repetição while
por uma repetição "for-of", tornando-a ainda mais organizada.
Outra sintaxe de função assíncrona
Já mostrei async function() {}
, mas a palavra-chave async
pode ser
usada com outra sintaxe de função:
Funções de seta
// map some URLs to json-promises
const jsonPromises = urls.map(async (url) => {
const response = await fetch(url);
return response.json();
});
Métodos do objeto
const storage = {
async getAvatar(name) {
const cache = await caches.open('avatars');
return cache.match(`/avatars/${name}.jpg`);
}
};
storage.getAvatar('jaffathecake').then(…);
Métodos da classe
class Storage {
constructor() {
this.cachePromise = caches.open('avatars');
}
async getAvatar(name) {
const cache = await this.cachePromise;
return cache.match(`/avatars/${name}.jpg`);
}
}
const storage = new Storage();
storage.getAvatar('jaffathecake').then(…);
Tenha cuidado! Evite ser sequencial demais
Embora esteja escrevendo um código que pareça síncrono, certifique-se de não perder o oportunidade de fazer coisas em paralelo.
async function series() {
await wait(500); // Wait 500ms…
await wait(500); // …then wait another 500ms.
return 'done!';
}
O processo acima leva 1.000 ms para ser concluído, enquanto:
async function parallel() {
const wait1 = wait(500); // Start a 500ms timer asynchronously…
const wait2 = wait(500); // …meaning this timer happens in parallel.
await Promise.all([wait1, wait2]); // Wait for both timers in parallel.
return 'done!';
}
O processo acima leva 500 ms para ser concluído, porque as duas esperas acontecem ao mesmo tempo. Vamos conferir um exemplo prático.
Exemplo: como gerar buscas em ordem
Digamos que você queira buscar uma série de URLs e registrá-los o mais rápido possível no na ordem correta.
Respire fundo: eis como isso fica com promessas:
function markHandled(promise) {
promise.catch(() => {});
return promise;
}
function logInOrder(urls) {
// fetch all the URLs
const textPromises = urls.map((url) => {
return markHandled(fetch(url).then((response) => response.text()));
});
// log them in order
return textPromises.reduce((chain, textPromise) => {
return chain.then(() => textPromise).then((text) => console.log(text));
}, Promise.resolve());
}
Sim, isso mesmo. Estou usando reduce
para encadear uma sequência de promessas. Estou tão
inteligentes. Mas essa é uma codificação tão inteligente, que fica melhor sem ela.
No entanto, ao converter o item acima para uma função assíncrona, é tentador ir sequencial demais:
async function logInOrder(urls) { for (const url of urls) { const response = await fetch(url); console.log(await response.text()); } }
function markHandled(...promises) { Promise.allSettled(promises); } async function logInOrder(urls) { // fetch all the URLs in parallel const textPromises = urls.map(async (url) => { const response = await fetch(url); return response.text(); }); markHandled(...textPromises); // log them in sequence for (const textPromise of textPromises) { console.log(await textPromise); } }
Solução alternativa para suporte do navegador: geradores
Se você segmentar navegadores compatíveis com geradores (que inclui a versão mais recente de todos os principais navegadores ) pode usar funções assíncronas com polyfill.
O Babel fará isso por você, Confira um exemplo do Babel REPL
- observe como o código transcompilado é semelhante. Essa transformação faz parte Predefinição do Babel es2017.
Recomendamos a abordagem de transcompilação, porque você pode apenas desativá-la quando seu navegadores de destino oferecem suporte a funções assíncronas, mas se você realmente não quiser usar um transpilador, você pode usar polyfill do Babel (em inglês) e use por conta própria. Em vez de:
async function slowEcho(val) {
await wait(1000);
return val;
}
...você incluiria o polyfill e escrever:
const slowEcho = createAsyncFunction(function* (val) {
yield wait(1000);
return val;
});
Você precisa transmitir um gerador (function*
) para createAsyncFunction
,
e usar yield
em vez de await
. Fora isso, funciona da mesma forma.
Alternativa: regenerador
Se você segmentar navegadores mais antigos, o Babel também pode transcompilar geradores, permitindo que você use funções assíncronas até o IE8. Para fazer isso, você precisa de: Predefinição do Babel es2017 e a predefinição es2015.
O resultado não é tão bonito, então cuidado uso excessivo de código.
Sincronize tudo!
Quando as funções assíncronas estiverem em todos os navegadores, use-as em todos os que retorna promessa. Elas não apenas tornam o código mais ordenado, mas também que a função sempre retornará uma promessa.
Eu me empolgei com as funções assíncronas de volta 2014 e é ótimo vê-los aparecer, de verdade, em navegadores. Opa!