Espere a que una función termine en JavaScript

Moataz Farid 12 octubre 2023
  1. Sync y Async en JavaScript
  2. Use callback para esperar a que una función termine en JavaScript
  3. Usa promises para esperar a que una función termine en JavaScript
  4. Use async/await para esperar a que una función termine antes de continuar la ejecución
Espere a que una función termine en JavaScript

Este tutorial introducirá JavaScript Callbacks, Promises, y Async/await y le mostrará cómo esperar a que una función de asíncrono termine antes de continuar la ejecución.

Para entender lo que son Promises y Async/await, primero necesitamos entender lo que las funciones Sync y Async están en JavaScript.

Sync y Async en JavaScript

La programación sincrónica ejecuta un comando a la vez. Cuando llamamos a una función que realiza una acción de larga duración, detendrá el programa hasta que termine.

JavaScript es tradicionalmente de un solo subproceso, incluso con multi-núcleos. Podemos hacer que ejecute tareas sólo en un único hilo llamado hilo principal.

Este comportamiento sincrónico es un factor limitante en los multi-hilos, pero ayuda al usuario a escribir códigos sin preocuparse por problemas de concurrencia.

Mientras que en la programación asíncrona, la acción de larga duración se ejecutará en otro hilo que no sea el principal, por lo que la ejecución principal no se bloquea. Cuando esa función de larga duración termina, el programa principal es informado y obtiene acceso a los resultados.

La mayoría de las primitivas de E/S en JavaScript no se bloquean. Las solicitudes de red, las operaciones del sistema de archivos son todas operaciones no bloqueantes. Estar bloqueado es la excepción en JavaScript.

Ya que JavaScript se basa en técnicas de programación asíncrona, hay múltiples enfoques como callbacks, promises y async/wait que permiten poner en secuencia las ejecuciones de las funciones. De modo que un bloque de código o una función no se ejecutará antes de que termine otra función específica.

sync y async

La figura anterior muestra la clara variación entre la ejecución asíncrona y síncrona de dos funciones.

Use callback para esperar a que una función termine en JavaScript

Si tenemos declaraciones sincrónicas, entonces ejecutar esas declaraciones una tras otra es sencillo.

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

Supongamos que queremos ejecutar dos funciones, functionOne() y functionTwo(), de tal manera que functionOne() debe ser ejecutada después de ejecutar algunas declaraciones asincrónicas 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();

Al ejecutar el código anterior, lo último que se imprime en la consola es I am a callback. El famoso ejemplo de callback es la función setTimeout con una función de manejo que se ejecuta después del timing out.

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

Usa promises para esperar a que una función termine en JavaScript

Una promises es un objeto que representa el eventual cumplimiento o fracaso de una operación asíncrona. Adjuntamos la llamada de cumplimiento a la promises con una o más declaraciones de then, y cuando se puede llamar a la llamada del manejador de errores en la catch.

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

Encadenar las declaraciones de then y catch como en el ejemplo anterior es una de las ventajas de las promesas. Prometemos doSecondThing(result) una vez que la doFirstThing() se cumpla. Los argumentos del then son opcionales, pero obligatorios si tienes que devolver un resultado.

En caso de que haya un error, el navegador buscará al final de la cadena el catch y lo ejecutará. Es muy similar a la famosa try-catch.

El siguiente ejemplo nos ayudará a entender las cadenas de promises y nos mostrará cómo podemos esperar a que una función con comportamiento asíncrono termine su ejecución antes de poder continuar con ella.

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

Crear una promesa puede ser tan fácil como devolver una nueva Promise(). El constructor Promise() recibe una función como argumento, que debe tener dos parámetros - resolve y reject.

En caso de que nuestro evento se cumpla, y necesitemos devolver el resultado, usamos la función resolve() cuando tenemos éxito en lo que estábamos haciendo. Pero si ocurre un error y necesita ser manejado, usamos reject() para enviar el error al catch.

Ponemos la bandera resolvedFlag = true para simular el manejo del error en el catch. Si resolvedFlag se establece como false, se llama a la función reject() y el error se maneja en el bloque catch.

Resultado:

Entered function
Inside the promise
Handling error as we received Rejected

Use async/await para esperar a que una función termine antes de continuar la ejecución

Otra forma de esperar a que una función se ejecute antes de continuar la ejecución en el entorno asíncrono en JavaScript es usar async/await.

La función async es la función que es declarada por la palabra clave async, mientras que sólo la palabra clave await se permite dentro de la función async y se usa para suspender el progreso dentro de la función async hasta que la operación asíncrona basada en la promesa se cumpla o se rechace.

Las palabras clave async/await permiten un comportamiento asincrónico basado en la promesa en un estilo más limpio.

Entendamos cómo funciona async/await. La función que estamos esperando debería devolver una instancia de la clase Promise para esperar que se ejecute usando la palabra clave await antes de llamarla. Como se mencionó anteriormente, la función que contiene la declaración await debe ser declarada con la declaración async.

El siguiente ejemplo muestra cómo esperar a que esa función basada en la promesa termine antes de continuar la ejecución.

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

Artículo relacionado - JavaScript Promises