A construção assíncrona / aguardada apareceu no padrão ES7. Pode ser considerada uma melhoria notável no campo da programação assíncrona em JavaScript. Ele permite que você escreva um código que pareça síncrono, mas é usado para resolver tarefas assíncronas e não bloqueia o encadeamento principal. Apesar do fato de que assíncrono / espera é um ótimo recurso novo do idioma, usá-lo corretamente não é tão simples. O material, cuja tradução publicamos hoje, é dedicado a um estudo abrangente de assíncrono / espera e uma história sobre como usar esse mecanismo de maneira correta e eficaz.

Pontos fortes de assíncrono / aguardar
O benefício mais importante que um programador usando a construção assíncrona / espera obtém é que torna possível escrever código assíncrono em um estilo específico para o código síncrono. Compare o código escrito usando async / waitit com o código baseado em promessas.
// async/await async getBooksByAuthorWithAwait(authorId) {  const books = await bookModel.fetchAll();  return books.filter(b => b.authorId === authorId); } //  getBooksByAuthorWithPromise(authorId) {  return bookModel.fetchAll()    .then(books => books.filter(b => b.authorId === authorId)); } 
É fácil perceber que a versão assíncrona / aguardada do exemplo é mais compreensível do que sua versão, na qual a promessa é usada. Se você não prestar atenção à palavra-chave 
await , esse código será semelhante a um conjunto regular de instruções executadas de forma síncrona - como no JavaScript familiar ou em qualquer outra linguagem síncrona como Python.
A atratividade de async / waitit não se deve apenas à maior legibilidade do código. Além disso, esse mecanismo possui excelente suporte ao navegador, o que não requer nenhuma solução alternativa. Portanto, hoje as funções assíncronas são totalmente compatíveis com todos os principais navegadores.
Todos os principais navegadores suportam funções assíncronas ( caniuse.com )Esse nível de suporte significa, por exemplo, que o código usando async / waitit não precisa ser 
transposto . Além disso, facilita a depuração, o que talvez seja ainda mais importante do que a falta de necessidade de transpilação.
A figura a seguir mostra o processo de depuração de uma função assíncrona. Aqui, ao definir um ponto de interrupção na primeira instrução da função e ao executar o comando Step Over, quando o depurador atingir a linha em que a palavra-chave 
await é usada, você poderá observar como o depurador pausa por um tempo, aguardando o 
bookModel.fetchAll() função 
bookModel.fetchAll() e, em seguida, pula para a linha em que o comando 
.filter() é 
.filter() ! Esse processo de depuração parece muito mais simples do que as promessas de depuração. Aqui, ao depurar código semelhante, você teria que definir outro ponto de interrupção na linha 
.filter() .
 Depurando uma função assíncrona. O depurador aguardará a conclusão da linha de espera e passará para a próxima linha após a conclusão da operação
Depurando uma função assíncrona. O depurador aguardará a conclusão da linha de espera e passará para a próxima linha após a conclusão da operaçãoOutro ponto forte do mecanismo em consideração, que é menos óbvio do que o que já examinamos, é a presença da palavra 
async chave 
async aqui. No nosso caso, seu uso garante que o valor retornado por 
getBooksByAuthorWithAwait() seja uma promessa. Como resultado, você pode usar com segurança a construção 
getBooksByAuthorWithAwait().then(...) ou 
await getBooksByAuthorWithAwait() construção 
await getBooksByAuthorWithAwait() no código que chama essa função. Considere o seguinte exemplo (observe que isso não é recomendado):
 getBooksByAuthorWithPromise(authorId) { if (!authorId) {   return null; } return bookModel.fetchAll()   .then(books => books.filter(b => b.authorId === authorId)); } } 
Aqui, a função 
getBooksByAuthorWithPromise() pode, se estiver tudo bem, retornar uma promessa ou, se algo der errado - 
null . Como resultado, se ocorrer um erro, você não poderá chamar 
.then() com segurança 
.then() . Ao declarar funções usando a 
async erros desse tipo são impossíveis.
Sobre a percepção errônea de assíncrono / aguardar
Em algumas publicações, a construção assíncrona / espera é comparada às promessas e diz-se que representa a próxima geração da evolução da programação JavaScript assíncrona. Com isso, com todo o respeito devido aos autores de tais publicações, permito-me discordar. O assíncrono / espera é uma melhoria, mas não passa de "açúcar sintático", cuja aparência não leva a uma mudança completa no estilo de programação.
Em essência, funções assíncronas são promessas. Antes de um programador poder usar adequadamente a construção assíncrona / aguardada, ele deve estudar bem as promessas. Além disso, na maioria dos casos, trabalhando com funções assíncronas, você precisa usar promessas.
Dê uma olhada nas 
getBooksByAuthorWithAwait() e 
getBooksByAuthorWithPromises() do exemplo acima. Observe que eles são idênticos não apenas em termos de funcionalidade. Eles também têm exatamente as mesmas interfaces.
Tudo isso significa que se você chamar diretamente a função 
getBooksByAuthorWithAwait() , ela retornará a promessa.
De fato, a essência do problema que estamos falando aqui é a percepção incorreta do novo design, quando cria uma sensação enganosa de que uma função síncrona pode ser convertida em assíncrona devido ao uso simples do 
async e 
await palavras-chave e não pensar em mais nada.
Armadilhas do assíncrono / aguardam
Vamos falar sobre os erros mais comuns que podem ser cometidos usando async / waitit. Em particular, sobre o uso irracional de chamadas sucessivas de funções assíncronas.
Embora a palavra-chave 
await possa fazer com que o código pareça síncrono, use-o, vale lembrar que o código é assíncrono, o que significa que você precisa ter muito cuidado com a chamada seqüencial de funções assíncronas.
 async getBooksAndAuthor(authorId) { const books = await bookModel.fetchAll(); const author = await authorModel.fetch(authorId); return {   author,   books: books.filter(book => book.authorId === authorId), }; } 
Esse código, em termos de lógica, parece correto. No entanto, há um problema sério. É assim que funciona.
- As chamadas do sistema await bookModel.fetchAll()e aguardam a.fetchAll()comando.fetchAll().
- Após receber o resultado de bookModel.fetchAll()await authorModel.fetch(authorId)será chamado.
Observe que a chamada para 
authorModel.fetch(authorId) é independente dos resultados da chamada para 
bookModel.fetchAll() e, de fato, esses dois comandos podem ser executados em paralelo. No entanto, o uso de 
await resulta nessas duas chamadas sendo executadas seqüencialmente. O tempo total de execução seqüencial desses dois comandos será maior que o tempo de execução paralela.
Aqui está a abordagem correta para escrever esse código:
 async getBooksAndAuthor(authorId) { const bookPromise = bookModel.fetchAll(); const authorPromise = authorModel.fetch(authorId); const book = await bookPromise; const author = await authorPromise; return {   author,   books: books.filter(book => book.authorId === authorId), }; } 
Considere outro exemplo do uso indevido de funções assíncronas. Isso ainda é pior do que no exemplo anterior. Como você pode ver, para carregar de forma assíncrona uma lista de certos elementos, precisamos confiar nas possibilidades de promessas.
 async getAuthors(authorIds) { //  ,     // const authors = _.map( //   authorIds, //   id => await authorModel.fetch(id)); //   const promises = _.map(authorIds, id => authorModel.fetch(id)); const authors = await Promise.all(promises); } 
Em poucas palavras, para usar corretamente funções assíncronas, você precisa, como no momento em que isso não era possível, primeiro pense em operações assíncronas e depois escreva o código usando 
await . Em casos complexos, provavelmente será mais fácil usar promessas diretamente.
Tratamento de erros
Ao usar promessas, a execução do código assíncrono pode terminar conforme o esperado - eles dizem que a promessa foi resolvida com sucesso ou com um erro - e dizem que a promessa foi rejeitada. Isso nos permite usar 
.then() e 
.catch() , respectivamente. No entanto, o tratamento de erros usando o mecanismo assíncrono / espera pode ser complicado.
▍ construção try / catch
A maneira padrão de lidar com erros ao usar async / waitit é com a construção try / catch. Eu recomendo usar essa abordagem. Ao fazer uma chamada em espera, o valor retornado quando a promessa é rejeitada é apresentado como uma exceção. Aqui está um exemplo:
 class BookModel { fetchAll() {   return new Promise((resolve, reject) => {     window.setTimeout(() => { reject({'error': 400}) }, 1000);   }); } }  
O erro detectado no 
catch é exatamente o valor obtido quando a promessa é rejeitada. Depois de capturar uma exceção, podemos aplicar várias abordagens para trabalhar com ela:
- Você pode manipular a exceção e retornar o valor normal. Se você não usar a expressão de returnnocatchpara retornar o que é esperado após a execução da função assíncrona, isso será equivalente ao uso do comandoreturn undefined;
- Você pode simplesmente passar o erro para o local onde o código que falhou foi chamado e permitir que ele seja processado lá. Você pode gerar um erro diretamente usando um comando como throw error;, que permite usar a funçãoasync getBooksByAuthorWithAwait()na cadeia de promessas. Ou seja, ele pode ser chamado usando a construçãogetBooksByAuthorWithAwait().then(...).catch(error => ...). Além disso, você pode agrupar o erro em um objetoError, que pode parecerthrow new Error(error). Isso permitirá, por exemplo, ao enviar informações de erro para o console, exibir a pilha de chamadas completa.
- O erro pode ser representado como uma promessa rejeitada, parece return Promise.reject(error). Nesse caso, isso é equivalente ao comandothrow error, não sendo recomendado.
Aqui estão os benefícios do uso da construção try / catch:
- Tais ferramentas de tratamento de erros existem na programação há muito tempo, são simples e compreensíveis. Digamos, se você tiver experiência em programação em outras linguagens, como C ++ ou Java, entenderá facilmente o dispositivo try / catch em JavaScript.
- Você pode fazer várias chamadas em espera em um bloco try / catch, o que permite lidar com todos os erros em um só lugar, se você não precisar lidar separadamente com erros em cada etapa da execução do código.
Note-se que há uma desvantagem no mecanismo try / catch. Como try / catch captura todas as exceções que ocorrem no bloco 
try , essas exceções não relacionadas a promessas também serão inseridas no manipulador de 
catch . Dê uma olhada neste exemplo.
 class BookModel { fetchAll() {   cb();    //    ,   `cb`  ,       return fetch('/books'); } } try { bookModel.fetchAll(); } catch(error) { console.log(error);  //       "cb is not defined" } 
Se você executar esse código, verá a mensagem de erro 
ReferenceError: cb is not defined no console. Esta mensagem é emitida pelo comando 
console.log() do 
catch , e não pelo próprio JavaScript. Em alguns casos, esses erros levam a graves consequências. Por exemplo, se chamar 
bookModel.fetchAll(); Se você estiver oculto em uma série de chamadas de função e uma das chamadas "engolir" um erro, será muito difícil detectar esse erro.
Return Retorno de função de dois valores
A inspiração para a próxima maneira de lidar com erros no código assíncrono é Go. Ele permite que funções assíncronas retornem um erro e um resultado. Leia mais sobre isso 
aqui .
Em poucas palavras, funções assíncronas, com esta abordagem, podem ser usadas assim:
 [err, user] = await to(UserModel.findById(1)); 
Pessoalmente, não gosto disso, porque esse método de tratamento de erros introduz o estilo de programação Go no JavaScript, que parece não natural, embora, em alguns casos, possa ser muito útil.
▍Utilização de .catch
A maneira final de lidar com os erros, sobre os quais falaremos, é usar 
.catch() .
Pense em como a 
await funciona. Ou seja, o uso dessa palavra-chave faz com que o sistema aguarde até que a promessa conclua seu trabalho. Além disso, lembre-se de que um comando no formato 
promise.catch() também retorna uma promessa. Tudo isso sugere que erros de função assíncrona podem ser manipulados assim:
 // books   undefined   , //    catch     let books = await bookModel.fetchAll() .catch((error) => { console.log(error); }); 
Dois pequenos problemas são característicos dessa abordagem:
- Essa é uma mistura de promessas e funções assíncronas. Para usar isso, é necessário, como em outros casos semelhantes, entender as características do trabalho das promessas.
- Essa abordagem não é intuitiva, pois o tratamento de erros é realizado em um local incomum.
Sumário
A construção assíncrona / aguardada, que foi introduzida no ES7, é definitivamente uma melhoria nos mecanismos de programação assíncrona do JavaScript. Isso pode facilitar a leitura e o código de depuração. No entanto, para usar corretamente o assíncrono / aguardar, é necessário um profundo entendimento das promessas, uma vez que o assíncrono / aguardar é apenas "açúcar sintático" baseado em promessas.
Esperamos que este material tenha permitido que você se familiarize com async / wait, e o que você aprendeu aqui o salvará de alguns erros comuns que surgem ao usar essa construção.
Caros leitores! Você usa a construção assíncrona / espera no JavaScript? Nesse caso, informe-nos como você lida com erros no código assíncrono.
