Por que eu devo ler este artigo:Este artigo é útil para desenvolvedores que desejam adicionar recursos assíncronos em suas aplicações sem necessariamente usar Ajax.

O Ajax resolve muitos dos problemas quando precisamos nos comunicar com um servidor e trocar requisições e respostas, porém não se mostra muito bom para lidar com tais manipulações dentro do código JavaScript no cliente.

Você verá o que são as Promises e como elas podem ser usadas para atacar esse problema, bem como suas principais características, prós e contras e como você pode fazer a integração das mesmas com frameworks como Bluebird e jQuery.

O universo front-end está acostumado a lidar com JavaScript assíncrono em toda parte. Ajax, WebRTC e Node.js são alguns exemplos de onde APIs assíncronas são encontradas. Embora seja fácil de escrever uma função rápida para lidar com o resultado de uma solicitação HTTP, também é fácil se perder em um mar imprevisível de retornos de chamadas à medida que uma base de código cresce e mais pessoas passam a contribuir. É aí que uma boa abordagem para lidar com código assíncrono entra e muitos desenvolvedores estão escolhendo usar as Promises para tal abordagem.

Aplicações Web carregam dados e scripts no navegador de forma assíncrona. O Node.js e seus derivados fornecem uma série de APIs para I/O assíncrono.

E novas especificações da web para Streams, Service Workers, e Font Loading (Carregamento de Fontes) incluem chamadas assíncronas em suas especificações. Estes avanços ampliam as capacidades de aplicações JavaScript, mas usá-los sem compreender como os trabalhos assíncronos funcionam pode resultar em código imprevisível e difícil de manter.

Essa parte do ciclo de software é importante pois sem ela as coisas podem funcionar como esperado em ambientes de desenvolvimento ou teste, mas falhar quando implantadas em produção por causa de variáveis como velocidade da rede ou o desempenho do hardware.

O maior desafio de se construir código JavaScript assíncrono é coordenar a ordem de execução das funções e lidar com o gerenciamento de erros que podem (e vão) ser lançados.

Se um erro é lançado durante uma execução síncrona, podemos capturá-lo, tratá-lo e prosseguir com a execução normalmente, porém se o mesmo acontece num processamento assíncrono, não teremos como saber em que parte do código aconteceu e no máximo uma mensagem no Console aparecerá, além do fato de uma execução importante para o sistema não ter funcionado e não sabermos o que houve lá.

As Promises encapsulam esse problema fornecendo formas para organizar callbacks (funções de retorno) que são fáceis de implementar e manter.

Além disso, quando erros acontecem podemos gerenciá-los de fora da lógica primária da aplicação sem a necessidade de código sujo para checar se algo de errado aconteceu há cada novo passo da execução.

Uma promise é um objeto que serve como uma “lacuna” para um valor. Esse valor é usualmente o resultado de uma operação assíncrona como uma requisição HTTP ou a leitura de um arquivo no disco. Quando uma função assíncrona é chamada ela pode imediatamente retornar um objeto Promise. Usando esse objeto, você pode registrar callbacks que executarão quando a operação ocorrer com sucesso ou com erros.

Neste artigo trataremos de explorar os principais recursos, funções e gerenciamento de estado das promises. Você verá como usar todo o potencial da API de comunicação assíncrona, bem como entender, via exemplos básicos, quais os melhores fluxos de uso de uma promise. Também entenderemos o que são web workers, objetos deferidos e como eles se integram com as promises e com outras bibliotecas como o jQuery.

JavaScript assíncrono

Vamos começar com um trecho de código muito comum nos projetos JavaScript. O código da Listagem 1 faz uma solicitação HTTP usando o objeto XMLHttpRequest (XHR) e usa um loop while que se estende por três segundos. Embora seja geralmente uma má prática implementar “um atraso” com o loop while, é uma boa maneira de ilustrar como o JavaScript é executado. Veremos no código quando o callback do listener será disparado.

Listagem 1. Exemplo de código assíncrono com XHR.


  01 // Faz um request assíncrono
  02 var async = true;
  03 var xhr = new XMLHttpRequest();
  04 xhr.open('get', 'dados.json', async);
  05 xhr.send();
  06 
  07 // Cria um timing de três segundos
  08 var timestamp = Date.now() + 3000;
  09 while (Date.now() < timestamp);
  10 
  11 // Quando os três segundos passarem,
  12 // add um listener aos eventos de xhr.load e xhr.error
  13 function listener() {
  14      console.log('Boas vindas do listener');
  15 } 
  16 xhr.addEventListener('load', listener);
  17 xhr.addEventListener('error', listener);

O código cria um novo objeto XHR que representa a base de tudo que é Ajax no JavaScript, logo mesmo APIs que encapsulam requisições Ajax simplificadas, como o jQuery por exemplo, usam esses objetos por detrás.

Na linha 4 abrimos uma nova conexão usando o método HTTP “GET” (já que se trata do modelo request-response tradicional do HTTP, porém assíncrono, sem loading de página) e informamos o endereço final para onde a requisição será enviada. Você pode substituir esse valor por qualquer outro endpoint que reconheça o request.

Na linha 5 enviamos a requisição. Precisamos aguardar até que ela retorne para processar o resultado. Como o JavaScript, diferente de outras linguagens server side, não espera um método retornar para prosseguir a execução no chamador, criamos um loop de três segundos (linhas 8 e 9) via objeto Date do JavaScript. Após isso, quando retornar adicionamos o listener e cadastramos seu método ouvinte para quando estiver carregado (“load”) e algum erro estourar (“error”) (linhas 16 e 17).

O leitor pode se perguntar se o listener será mesmo “sempre” chamado no exemplo anterior, já que o timing via while também não é uma opção 100% garantida.

Mas sim, ele será sempre chamado porque o callback assume prioridade na execução para o JavaScript, uma vez que ele precisa dar um retorno sempre da chamada à função.

As funções de callback, por sua vez, podem ser invocadas tanto de forma síncrona quanto assíncrona (isto é, antes ou depois da função para qual foram passadas retornar). Vejamos na Listagem 2 dois exemplos de cada uma dessas formas.

Listagem 2. Exemplos de callbacks síncrono e assíncrono.


  01 // Callback síncrono
  02 function callback(msg) {
  03      console.log(msg);
  04 }
  05 msgs.forEach(callback);
  06 
  07 // Callback assíncrono
  08 function realocarElemento() {
  09      console.log('realocando...');
  10 // ...
  11 }
  12 window.requestAnimationFrame(realocarElemento);
  13 console.log('Última linha do script');
  14 // Saída no Console:
  15 // Última linha do script
  16 // realocando...

Na primeira função (linha 2) a função callback() recebe uma string msg para exibir ...

Quer ler esse conteúdo completo? Tenha acesso completo