Esperar por uma função para terminar em JavaScript

Moataz Farid 12 outubro 2023
  1. Sync e Async em JavaScript
  2. Utilize callback para esperar por uma função para terminar em JavaScript
  3. Utilize promises para Esperar por uma Função para Terminar em JavaScript
  4. Utilize async/await para esperar que uma função termine antes de continuar a execução
Esperar por uma função para terminar em JavaScript

Este tutorial irá introduzir JavaScript Callbacks, Promises, e Async/await e mostrar-lhe-á como esperar que uma função async termine antes de continuar a execução.

Para compreender o que são Promises e Async/await, precisamos primeiro de compreender o que são as funções Sync e Async em JavaScript.

Sync e Async em JavaScript

A programação síncrona executa um comando de cada vez. Quando chamamos uma função que executa uma acção de longa duração, ela pára o programa até que este termine.

O JavaScript é tradicionalmente de rosca única, mesmo com múltiplos núcleos. Podemos conseguir que execute tarefas apenas num único string chamado string principal.

Este comportamento sincronizado é um factor limitativo nos multi-tarefas, mas ajuda o utilizador a escrever códigos sem se preocupar com problemas de simultaneidade.

Enquanto na programação assíncrona, a acção de longo prazo será executada noutro string que não o string principal, pelo que a execução principal não é bloqueada. Quando essa função de longa duração termina, o programa principal é informado e tem acesso aos resultados.

A maioria das primitivas E/S em JavaScript não estão bloqueadas. Pedidos de rede, operações de sistemas de ficheiros são todas operações de não-bloqueio. Estar bloqueado é a excepção em JavaScript.

Uma vez que o JavaScript se baseia em técnicas de programação assíncrona, existem múltiplas abordagens tais como callbacks, promises e async/await, permitindo-lhe colocar as suas execuções de funções em sequência. Para que um bloco de código ou uma função não seja executado antes de outra função específica terminar.

sync e async

A figura acima mostra a clara variação entre a execução assíncrona e síncrona de duas funções.

Utilize callback para esperar por uma função para terminar em JavaScript

Se tivermos declarações síncronas, então a execução dessas declarações uma após a outra é directa.

function one() {
  console.log('I am function One');
}
function Two() {
  console.log('I am function Two');
}

one();
Two();

Resultado:

I am function One 
I am function Two

Suponhamos que queremos executar duas funções, functionOne() e functionTwo() de tal forma que functionOne() devam ser executadas após a execução de algumas instruções assíncronas dentro de functionTwo().

function functionOne(_callback) {
  // do some asynchronus work
  _callback();
}

function functionTwo() {
  // do some asynchronus work
  functionOne(() => {
    console.log('I am a callback');
  });
}

functionTwo();

Ao executar o código acima, a última coisa impressa na consola é I am a callback. O famoso exemplo callback é a função setTimeout com uma função de manipulação a executar depois de expirado o tempo.

function testCallBack() {
  console.log('log before use setTimeout function');
  setTimeout(() => {
    console.log('inside timeout');
  }, 5000);
  console.log('log after use setTimeout function');
}

testCallBack();

Resultado:

log before use setTimeout function
log after use setTimeout function
inside timeout

Utilize promises para Esperar por uma Função para Terminar em JavaScript

Um promises é um objecto que representa o eventual cumprimento ou falha de uma operação assíncrona. Anexamos a chamada de retorno do cumprimento ao promises com uma ou mais declarações then, e quando se pode chamar o manipulador do erro na catch.

doFirstThing()
    .then(result => doSecondThing(result))
    .then(newResult => doThirdThing(newResult))
    .then(finalResult => {
      console.log('The final result thing' + finalResult);
    })
    .catch(failureCallbackfunction);
}

Encadear as declarações de then e catch como o exemplo acima é uma das vantagens das promessas. Prometemos doSecondThing(result) uma vez que o doFirstThing() seja cumprido. Os argumentos para o then são opcionais, mas necessários se tiver de devolver um resultado.

No caso de haver um erro, o navegador irá olhar para o fim da string para o catch e executá-lo. É muito semelhante à famosa try-catch.

O exemplo seguinte irá ajudar-nos a compreender as strings de promises e mostrar-nos como podemos esperar que uma função com comportamento assíncrono termine a execução antes de podermos continuar a execução.

var resolvedFlag = true;

let mypromise = function functionOne(testInput) {
  console.log('Entered function');
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      console.log('Inside the promise');
      if (resolvedFlag == true) {
        resolve('Resolved');
      } else {
        reject('Rejected')
      }
    }, 2000);
  });
};

mypromise()
    .then((res) => {console.log(`The function recieved with value ${res}`)})
    .catch((error) => {
      console.log(`Handling error as we received ${error}`);
    });

Resultado:

Entered function
Inside the promise
The function received with value Resolved

Criar uma promessa pode ser tão fácil como devolver um novo Promessa(). O Promise() construtor recebe uma função como argumento, que deve ter dois parâmetros - resolver e rejeitar.

No caso do nosso evento ser cumprido, e precisamos de devolver o resultado, utilizamos a função resolve() quando somos bem sucedidos no que estávamos a fazer. Mas se um erro acontecer e precisar de ser tratado, utilizamos a função reject() para enviar o erro para a função catch.

Definimos a bandeira resolvedFlag = true para simular a manipulação do erro no catch. Se resolvedFlag estiver definida para ser false, a função reject() é chamada, e o erro é tratado no bloco catch.

Resultado:

Entered function
Inside the promise
Handling error as we received Rejected

Utilize async/await para esperar que uma função termine antes de continuar a execução

Outra forma de esperar que uma função seja executada antes de continuar a execução no ambiente assíncrono em JavaScript é utilizar async/wait.

A função async é a função que é declarada pela palavra-chave async, enquanto apenas a palavra-chave await é permitida dentro da função async e utilizada para suspender o progresso dentro da função assíncrona até que a operação assíncrona baseada na promessa seja cumprida ou rejeitada.

As palavras-chave async e await permitem um comportamento assíncrono, baseado na promessa, num estilo mais limpo.

Vamos compreender como funciona o async/await. A função pela qual estamos à espera deve devolver uma instância da classe Promise para esperar que ela seja executada utilizando a palavra-chave await antes de a chamar. Como mencionado acima, a função que contém a declaração async/await deve ser declarada com a declaração async.

O exemplo seguinte mostra como esperar que essa função baseada na promessa termine antes de continuar a execução.

function testAsync() {
  return new Promise((resolve, reject) => {
    // here our function should be implemented
    setTimeout(() => {
      console.log('Hello from inside the testAsync function');
      resolve();
      ;
    }, 5000);
  });
}

async function callerFun() {
  console.log('Caller');
  await testAsync();
  console.log('After waiting');
}

callerFun();

Resultado:

Caller
Hello from inside the testAsync function
After waiting