Cómo funcionan las devoluciones de llamada, las promesas y la espera asíncrona - JavaScript asíncrono

JavaScript asíncrono

Cómo funcionan las devoluciones de llamada, las promesas y la espera asíncrona - JavaScript asíncrono

JavaScript promociona la programación asincrónica como una característica. Esto significa que, si alguna acción toma un tiempo, su programa puede continuar haciendo otras cosas mientras se completa la acción. Una vez que se realiza esa acción, puede hacer algo con el resultado. Esto resulta ser una gran característica para la funcionalidad como la obtención de datos, pero puede ser confuso para los recién llegados. En JavaScript, tenemos algunas formas diferentes de manejar la asincronía: funciones de devolución de llamada, promesas y async-wait.

Funciones de devolución de llamada

Una función de devolución de llamada es una función que usted proporciona que se ejecutará después de completar la operación asincrónica. Creemos un buscador de datos de usuario falso y usemos una función de devolución de llamada para hacer algo con el resultado.

El buscador de datos falsos

Primero creamos un buscador de datos falsos que no toma una función de devolución de llamada. Como fakeDatano existe durante 300 milisegundos, no tenemos acceso sincrónico a él.

const fetchData = userId => {
  setTimeout(() => {
    const fakeData = {
      id: userId,
      name: 'George',
    };
    // Our data fetch resolves
    // After 300ms. Now what?
  }, 300);
};

¡Para poder hacer algo con nuestro fakeData, podemos pasar fetchDatauna referencia a una función que manejará nuestros datos!

const fetchData = (userId, callback) => {
  setTimeout(() => {
    const fakeData = {
      id: userId,
      name: 'George',
    };
    callback(fakeData);
  }, 300);
};

Creemos una función de devolución de llamada básica y probémosla:

const cb = data => {
  console.log("Here's your data:", data);
};

fetchData(5, cb);

Después de 300 ms, deberíamos ver lo siguiente registrado:

Here's your data: {id: 5, name: "George"}

Promesas

El objeto Promise representa la finalización eventual de una operación en JavaScript. Las promesas pueden resolvebien rejectCuando se resuelve una Promesa, puede manejar su valor devuelto con el método. Si se rechaza una Promesa, puede utilizar la captura del error y manejarlo.

La sintaxis del objeto Promise es la siguiente:

new Promise(fn);

Were fnes una función que toma una resolvefunción y, opcionalmente, una rejectfunción.

fn = (resolve, reject) => {};

El buscador de datos falsos (con promesas)

Usemos el mismo buscador de datos falsos que antes. En lugar de pasar una devolución de llamada, vamos a devolver un nuevo Promiseobjeto que se resuelve con los datos de nuestro usuario después de 300 ms. Como beneficio adicional, también podemos darle una pequeña posibilidad de rechazo.

const fetchData = userId => {
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      if (Math.random() < 0.1) {
        reject('Fetch failed!');
      }
      const fakeData = {
        id: userId,
        name: 'George',
      };
      resolve(fakeData);
    }, 300);
  });
};

Nuestra nueva fetchDatafunción se puede utilizar de la siguiente manera:

fetchData(5)
  .then(user => {
    console.log("Here's your data:", user);
  })
  .catch(err => {
    console.error(err);
  });

Si fetchDatase resuelve con éxito (esto sucederá el 90% del tiempo), registraremos nuestros datos de usuario como lo hicimos con la solución de devolución de llamada. Si se rechaza, aparecerá console.errorel mensaje de error que creamos ("¡Fetch falló!")

Una cosa buena de Promesas es que puedes encadenarlas para ejecutar Promesas posteriores. Por ejemplo, podríamos hacer algo como esto:

fetchData(5)
  .then(user => {
    return someOtherPromise(user);
  })
  .then(data => {
    console.log(data);
  })
  .catch(err => {
    console.error(err);
  });

Además, podemos pasar una serie de Promesas Promise.alla solo tomar medidas después de que se resuelvan todas las Promesas:

Promise.all([fetchData(5), fetchData(10)])
  .then(users => {
    console.log("Here's your data:", users);
  })
  .catch(err => {
    console.error(err);
  });

En este caso, si ambas promesas se resuelven con éxito, se registrará lo siguiente:

Here's your data:
[{ id: 5, name: "George" }, { id: 10, name: "George" }]

Async-Await

Async-await ofrece una sintaxis diferente para escribir promesas que algunos encuentran más claras. Con async-await, puede crear una asyncfunción. Dentro de esa función asíncrona, ¡puede ser awaitel resultado de una Promesa antes de ejecutar el código posterior! Veamos nuestro ejemplo de búsqueda de datos.

const fetchUser = async userId => {
  const user = await fetchData(userId);
  console.log("Here's your data:", user);
};
fetchUser(5);

Bastante bien, ¿verdad? Una pequeña arruga: no estamos manejando nuestro caso de rechazo de Promise. Podemos hacer esto con try/catch.

const fetchUser = async userId => {
  try {
    const user = await fetchData(userId);
    console.log("Here's your data:", user);
  } catch (err) {
    console.error(err);
  }
};
fetchUser(5);

Navegador / Soporte de nodo

Dado que las funciones de devolución de llamada son funciones normales que se pasan a otras funciones, no hay preocupación por el soporte. Las promesas han sido estándar desde ECMAScript 2015 y tienen un soporte decente, pero no son compatibles con Internet Explorer. Async-await es más nuevo (estándar desde ECMAScript 2017) y tiene un buen soporte en las versiones más nuevas del navegador. Nuevamente, no es compatible con Internet Exporer.

En el lado del nodo, async-wait (y, por lo tanto, Promises) han sido bien respaldados desde la versión v7.6 de noviembre.