Espera de nível superior em JavaScript

Resumo : neste tutorial, você aprenderá sobre o await de nível superior do JavaScript e seus casos de uso.

Introdução à espera de nível superior do JavaScript

ES2020 introduziu o recurso de espera de nível superior que permite que um módulo se comporte como uma asyncfunção. Um módulo que importa o módulo await de nível superior aguardará o carregamento antes de avaliar seu corpo.

Para entender melhor o recurso de espera de nível superior, daremos um exemplo:

Neste exemplo, teremos três arquivos: index.html, app.mjse user.mjs:

Aqui está o arquivo de índice que usa o módulo app.mjs:

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>JavaScript Top-Level Await Demo</title>
</head>

<body>
    <div class="container"></div>
    <script type="module" src="app.mjs"></script>
</body>

</html>Linguagem de código:  HTML, XML  ( xml )

O seguinte mostra o user.mjsarquivo:

let users;

(async () => {
  const url = 'https://jsonplaceholder.typicode.com/users';
  const response = await fetch(url);
  users = await response.json();
})();

export { users };Linguagem de código:  JavaScript  ( javascript )

O user.mjsmódulo usa a API fetch para obter os usuários no formato JSON de uma API e exportá-los.

Como só podemos usar a awaitpalavra-chave dentro de uma asyncfunção (antes do ES2020), precisamos agrupar a chamada da API dentro de uma expressão de função assíncrona invocada imediatamente (IIAFE).

O seguinte mostra o app.mjsmódulo:

import { users } from './user.mjs';

function render(users) {
  if (!users) {
    throw 'The user list is not available';
  }

  const list = users
    .map((user) => {
      return `<li> ${user.name}(<a href="email:${user.email}">${user.email}</a>)</li>`;
    })
    .join('');

  return `<ol>${list}</ol>`;
}

const container = document.querySelector('.container');
try {
  container.innerHTML = render(users);
} catch (e) {
  container.innerHTML = e;
}
Linguagem de código:  JavaScript  ( javascript )

Como funciona.

Primeiro, importe usersdo user.mjsmódulo:

import { users } from './user.mjs';Linguagem de código:  JavaScript  ( javascript )

Segundo, crie uma render()função que renderize a lista de usuários em uma lista ordenada em formato HTML:

function render(users) {
  if (!users) {
    throw 'The user list is not available.';
  }

  const list = users
    .map((user) => {
      return `<li> ${user.name}(<a href="email:${user.email}">${user.email}</a>)</li>`;
    })
    .join('');

  return `<ol>${list}</ol>`;
}Linguagem de código:  JavaScript  ( javascript )

Terceiro, adicione a lista de usuários ao elemento HTML com a classe .container:

const container = document.querySelector('.container');
try {
  container.innerHTML = render(users);
} catch (e) {
  container.innerHTML = e;
}Linguagem de código:  JavaScript  ( javascript )

Se você abrir o index.html, verá a seguinte mensagem:

The user list is not available.Linguagem de código:  PHP  ( php )

O seguinte mostra o fluxo principal:

Exemplo de espera de nível superior de JavaScript

Neste fluxo:

  • Primeiro, app.mjsimporta o user.mjsmódulo.
  • Segundo, o user.mjsmódulo executa e faz uma chamada de API.
  • Terceiro, enquanto a segunda etapa ainda está em andamento, ele app.mjscomeça a usar os usersdados importados do user.mjsmódulo.

Como a etapa 2 não foi concluída, a usersvariável foi undefined. Portanto, você viu a mensagem de erro na página.

Gambiarra

Para corrigir o problema, você pode exportar um Promisedo user.mjsmódulo e aguardar a conclusão da chamada da API antes de usar seu resultado.

O seguinte mostra a nova versão do user.mjsmódulo:

let users;

export default (async () => {
  const url = 'https://jsonplaceholder.typicode.com/users';
  const response = await fetch(url);
  users = await response.json();
})();

export { users };Linguagem de código:  JavaScript  ( javascript )

Nesta nova versão, o modelo user.mjs exporta o userse a Promisecomo exportação padrão.

Nas app.mjsimportações promisee usersdo user.mjsarquivo e chama then()o método da promiseseguinte forma:

import promise, { users } from './user.mjs';

function render(users) {
  if (!users) {
    throw 'The user list is not available.';
  }
  let list = users
    .map((user) => {
      return `<li> ${user.name}(<a href="email:${user.email}">${user.email}</a>)</li>`;
    })
    .join(' ');

  return `<ol>${list}</ol>`;
}

promise.then(() => {
  let container = document.querySelector('.container');
  try {
    container.innerHTML = render(users);
  } catch (error) {
    container.innerHTML = error;
  }
});
Linguagem de código:  JavaScript  ( javascript )

Como funciona.

Primeiro, importe promisee usersdo user.mjsmódulo:

import promise, { users } from './user.mjs';Linguagem de código:  JavaScript  ( javascript )

Segundo, chame o then()método da promessa e aguarde a conclusão da chamada da API para usar seus resultados:

promise.then(() => {
  let container = document.querySelector('.container');
  try {
    container.innerHTML = render(users);
  } catch (error) {
    container.innerHTML = error;
  }
});Linguagem de código:  JavaScript  ( javascript )

Agora, se você abrir o index.html, verá uma lista de usuários. No entanto, você precisa saber a promessa certa de esperar por ela ao usar o módulo.

ES2022 introduziu nesta solução alternativa o módulo de espera de nível superior para resolver esse problema.

Usando o await de nível superior

Primeiro, altere user.mjspara o seguinte:

const url = 'https://jsonplaceholder.typicode.com/users';
const response = await fetch(url);
let users = await response.json();

export { users };Linguagem de código:  JavaScript  ( javascript )

Neste módulo, você pode usar a awaitpalavra-chave sem colocar uma instrução dentro de uma asyncfunção.

Segundo, importe os usuários do user.mjsmódulo e use-o:

import { users } from './user.mjs';

function render(users) {
  if (!users) {
    throw 'The user list is not available.';
  }
  let list = users
    .map((user) => {
      return `<li> ${user.name}(<a href="email:${user.email}">${user.email}</a>)</li>`;
    })
    .join(' ');

  return `<ol>${list}</ol>`;
}

let container = document.querySelector('.container');

try {
  container.innerHTML = render(users);
} catch (error) {
  container.innerHTML = error;
}
Linguagem de código:  JavaScript  ( javascript )

Nesse caso, o app.mjsmódulo aguardará user.mjsa conclusão do módulo antes de executar seu corpo.

Casos de uso de espera de nível superior de JavaScript

Quando você usa o await de nível superior? Aqui estão alguns casos de uso.

Caminho de dependência dinâmica

const words = await import(`/i18n/${navigator.language}`);Linguagem de código:  JavaScript  ( javascript )

Neste exemplo, o await de nível superior permite que os módulos usem valores de tempo de execução para decidir as dependências, o que é útil para os seguintes cenários:

  • Internacionalização (i18n)
  • Divisões no ambiente de desenvolvimento/produção.

Retorno de dependência

Nesse caso, você pode usar o await de nível superior para carregar um módulo de um servidor (cdn1). E se falhar, você pode carregá-lo de um servidor de backup (cdn2):

let module;
try {
  module = await import('https://cdn1.com/module');
} catch {
  module = await import('https://cdn2.com/module');
}Linguagem de código:  JavaScript  ( javascript )

Resumo

  • Um módulo await de nível superior atua como uma asyncfunção.
  • Quando um módulo importa um módulo de espera de nível superior, ele aguarda a conclusão do módulo de espera de nível superior antes de avaliar seu corpo.

Deixe um comentário

O seu endereço de email não será publicado. Campos obrigatórios marcados com *