cefet_web

JavaScript parte 7

Destructuring, módulos, promessas, async/await


Roteiro

  1. Destructuring
  2. Módulos
  3. Promessas
  4. Async/await

Destructuring

“Desmontando”
objetos e listas


Propriedades curtinhas


Motivação para destructuring


Destructuring um objeto (1/2)

  1. Às vezes queremos que uma função tenha 1+ valores de retorno
  2. Ou então um função que espera um objeto que possua um certo formato
// Exemplo (1): "desmontando"
let imgEl = doc...ector('#logo');
const { alt, src } = imgEl;

console.log(`alt = ${alt}`);
console.log(`src = ${src}`);










// do jeito old...
let imgEl = doc...ector('#logo');
const alt = imgEl.alt;
const src = imgEl.src;

console.log('alt = ' + alt);
console.log('src = ' + src);
// Exemplo (2): definindo o parâmetro de forma "destructured":
function desenhaGrafico({size = 'big', coords = {x: 0, y: 0}} = {}) {
  console.log(size, coords, radius);
  // desenha aqui...
  // 


}

desenhaGrafico({
  coords: { x: 18, y: 30 },
  size: 'small'
});



// do jeito old...
function desenhaGraficoES5(options) {
  options = options || {};
  var size = options.size || 'big';
  var coords = options.coords || { x: 0, y: 0 };
  console.log(size, coords);
  // desenha aqui...
}

desenhaGraficoES5({
  cords: { x: 18, y: 30 },
  size: 'small'
});

Destructuring arrays


Módulos

Organização de código


História da modularidade em JS

  1. Ao longo dos anos 2000, a lógica das aplicações migrou para o front-end
    • Muito mais código JavaScript
    • Necessidade de organização
  2. Problema:
    • Gestão de dependências “manual”
    • Poluição do namespace global
    • Ambientes JavaScript fora do navegador
  3. Diferentes soluções emergiram ao longo do tempo
    • AMD, CommonJS, UMD e Módulos ES6 🌟

Módulos ES6


Módulos ES6 no navegador


Exemplo de export e import

export const nome = 'quadrado';

export function desenha(ctx, tam, x, y, cor) {
  ctx.fillStyle = cor;
  ctx.fillRect(x, y, tam, tam);

  return {
    tamanho: tam,
    x,
    y,
    cor
  };
}

Exemplo de export e import

  1. import { nome, desenha } from './geometria/quad.js'
    import { area, perimetro } from './retangulos.js'
    
    let quad = desenha(canvasEl.ctx, 50, 50, 100, 'blue');
    area(quad.tamanho)
    perimetro(quad.tamanho)
    

import e export padrão


Importação dinâmica


Promessas

Programação assíncrona


Motivação para promessas

Problema ~ quando precisamos realizar várias chamadas assíncronas, podemos ter um callback hell: várias callbacks aninhadas - Dificulta a leitura e escrita - Suscetível a erros do programador - Trata erros apenas por callback, dificultando a legibilidade/manutenibilidade do código

Soluções ~ uso de promessas explicitamente ou com async/await


Exemplo usando a Star Wars API 🌐

Imprimir (4) todas as “pessoas”
(3) da mesma espécie do
(2) primeiro residente do
(1) planeta Naboo


Callback Hell (no JSFiddle)


Solução com Promises (no JSFiddle)

sendAjax('https://swapi.dev/api/planets/8')             // Naboo
  .then(planeta => sendAjax(planeta.residents[0]))      // R2-D2
  .then(residente => sendAjax(residente.species))       // Droid
  .then(especie => Promise.all(especie.people.map(pessoa => sendAjax(pessoa))))
  .then(pessoas => console.log(pessoas.map(p => p.name).join(', ')))
   // C-3PO, R2-D2, R5-D4, IG-88, BB8
  .catch(erro => console.error(`Deu ruim: ${erro}`));

Definição de Promise


Criando uma Promise (exemplo)


Async/Await

Assíncrono que parece síncrono


Motivação: problemas com promessas

  1. Promessas com .then encadeados reduzem (mas não acabam) com callback hell 🔥
  2. Há possibilidade de .catch não capturar exceção
    • Caso .catch seja atrasado (eg, devido a alguma espera na criação da promessa - exemplo)
  3. É difícil escrever um fluxo condicional em uma cadeia de promessas
  4. Depurar ainda fica um pouco difícil com promessas
  5. É possível aumentar a legibilidade, se o código parecer síncrono

Palavra-chave await

  1. await substitui o .then
  2. Parece síncrono, mas suspende execução até a promessa ser cumprida
    • E isso não bloqueia a execução do processo (ie, é assíncrono)

Retorno de: await funcao()

  1. O valor que é resolvido pela Promise é retornado pela função
  2. ⬆️ Exemplo: pegar dados de notícias, criar um template e mostrar
    • 3x operações assíncronas em sequência:
      mostraNoticia() << formata() << dados('noticia')
      

Valores intermediários


Tratando erros


Palavra-chave async

  1. São basicamente equivalentes:
    // com async
    async function responder() {
      return 42
    }
    // promise explícita
    function responder() {
      return Promise.resolve(42)
    }
    
  2. Função async sempre retorna uma Promise:
    async function hello() {
      return 'Hello';
    }
    
    
    const b = hello();
    console.log(b);
    // ❌ [object Promise]
    
    b.then(texto => 
      console.log(texto))
    // ✅ 'Hello'
    

async/await vs Promise

try {
  const planeta = await sendAjax('https://swapi.dev/api/planets/8') // Naboo
  const residnt = await sendAjax(planeta.residents[0])              // R2-D2
  const especie = await sendAjax(residnt.species)                   // Droid
  const pessoas = await Promise.all(especie.people.map(pessoa => sendAjax(pessoa)))
  console.log(pessoas.map(p => p.name).join(', '))
  // C-3PO, R2-D2, R5-D4, IG-88, BB8
} catch(erro) {
  console.error(`Deu ruim: ${erro}`)
}
sendAjax('https://swapi.dev/api/planets/8')                         // Naboo
  .then(planeta => sendAjax(planeta.residents[0]))                  // R2-D2
  .then(residnt => sendAjax(residnt.species))                       // Droid
  .then(especie => Promise.all(especie.people.map(pessoa => sendAjax(pessoa))))    
  .then(pessoas => console.log(pessoas.map(p => p.name).join(', ')))
   // C-3PO, R2-D2, R5-D4, IG-88, BB8
  .catch(erro => console.error(`Deu ruim: ${erro}`));

Callbacks

sendAjax('https://swapi.dev/api/planets/8', planeta  => { // Naboo
  sendAjax(planeta.residents[0], residente => {           // R2-D2
    sendAjax(residente.species, especie => {              // Droid
      // pega todas as "pessoas" dessa espécie
      for (let pessoa of especie.people) {
        sendAjax(pessoa, p => {
          console.log(p.name + ', ')
        })
        // C-3PO, R2-D2, R5-D4, IG-88, BB8,
      }
    })
  })
})

Promises

sendAjax('https://swapi.dev/api/planets/8')             // Naboo
  .then(planeta => sendAjax(planeta.residents[0]))      // R2-D2
  .then(residente => sendAjax(residente.species))       // Droid
  .then(especie => Promise.all(especie.people.map(pessoa => sendAjax(pessoa))))
  .then(pessoas => console.log(pessoas.map(p => p.name).join(', ')))
   // C-3PO, R2-D2, R5-D4, IG-88, BB8
  .catch(erro => console.error(`Deu ruim: ${erro}`));

async-await

try {
  const planeta = await sendAjax('https://swapi.dev/api/planets/8')   // Naboo
  const residnt = await sendAjax(planeta.residents[0])      		      // R2-D2
  const especie = await sendAjax(residnt.species)       	            // Droid
  const pessoas = await Promise.all(especie.people.map(pessoa => sendAjax(pessoa)))
  console.log(pessoas.map(p => p.name).join(', '))
  // C-3PO, R2-D2, R5-D4, IG-88, BB8
} catch(erro) {
  console.error(`Deu ruim: ${erro}`)
}

Cuidados com assincronia


Exemplo de espera desnecessária

function espera(tempo) {
  return new Promise(resolver => 
    setTimeout(resolver, tempo))
}

async function quem() {
  await espera(200)
  return '🤡'
}

async function oque() {
  await espera(300)
  return 'espreita'
}

async function onde() {
  await espera(500)
  return 'nas sombras'
}
async function mensagem1() { // ❌
  const a = await quem()
  const b = await oque()
  const c = await onde()

  console.log(`${a} ${b} ${c}`)
}
mensagem1() // depois de 1s: (esperou pra +)
// 🤡 espreita nas sombras


async function mensagem2() { // ✅
  const todas = Promise.all([quem(), oque(), onde()])
  const [a, b, c] = await todas 
  
  console.log(`${a} ${b} ${c}`)
}
mensagem2() // depois de 500ms:
// 🤡 espreita nas sombras

Top-level await


Referências

  1. Na MDN:
  2. Livro Eloquent JavaScript:
  3. Site Toptal