El conocimiento es el nuevo dinero.
Aprender es la nueva manera en la que inviertes
Acceso Cursos

Varias prácticas recomendadas para escribir un buen código JavaScript asíncrono

no-async-promise-executor .

· 5 min de lectura
Varias prácticas recomendadas para escribir un buen código JavaScript asíncrono

No se recomienda pasar una función async al constructor new Promise

// ❌
new Promise(async (resolve, reject) => {});

// ✅
new Promise((resolve, reject) => {});

En primer lugar, al utilizar async en el constructor de la Promesa, envolver una Promesa puede ser innecesario. Además, si la función async lanza una excepción, la instancia promise recién construida no reject, por lo que el error no puede ser capturado.

no-await-in-loop

No se recomienda utilizar await dentro de un bucle, ya que esto suele indicar que el programa no está aprovechando al máximo la naturaleza orientada a eventos de JavaScript.

// ❌
for (const url of urls) {
  const response = await fetch(url);
}

Se recomienda refactorizar estas tareas asíncronas para que se ejecuten de forma concurrente, lo que puede mejorar significativamente la eficiencia de ejecución del código.

// ✅
const responses = [];
for (const url of urls) {
  const response = fetch(url);
  responses.push(response);
}

await Promise.all(responses);

no-promise-executor-return

No se recomienda devolver un valor desde la función constructora Promise, ya que el valor devuelto es inutilizable y no afecta al estado de la Promise.

// ❌
new Promise((resolve, reject) => {
  return result;
});

Lo normal es pasar el valor de retorno a resolve, y si se produce un error, pasarlo a reject.

// ✅
new Promise((resolve, reject) => {
  resolve(result);
});

require-atomic-updates

No se recomienda combinar operaciones de asignación y el uso de await, ya que esto puede dar lugar a condiciones de carrera.

Mira el siguiente código, ¿cuál crees que será el valor final de totalPosts?

CPU
1 vCPU
MEMORIA
1 GB
ALMACENAMIENTO
10 GB
TRANSFERENCIA
1 TB
PRECIO
$ 4 mes
Para obtener el servidor GRATIS debes de escribir el cupon "LEIFER"
// ❌
let totalPosts = 0;

async function getPosts(userId) {
  const users = [{ id: 1, posts: 5 }, { id: 2, posts: 3 }];
  await sleep(Math.random() * 1000);
  return users.find((user) => user.id === userId).posts;
}

async function addPosts(userId) {
  totalPosts += await getPosts(userId);
}

await Promise.all([addPosts(1), addPosts(2)]);
console.log('Post count:', totalPosts);

totalPosts imprimirá 3 o 5, pero no 8. Puede probarlo usted mismo en el navegador.

El problema es que hay un intervalo de tiempo entre la lectura de totalPosts y la actualización de totalPosts. Esto puede conducir a una condición de carrera, donde las actualizaciones no se reflejan en el ámbito de la función actual cuando el valor se actualiza en llamadas a funciones separadas. Como resultado, ambas funciones añadirán sus resultados al valor inicial de 0 para totalPosts.

La forma correcta de evitar race conditions:

// ✅
let totalPosts = 0;

async function getPosts(userId) {
  const users = [{ id: 1, posts: 5 }, { id: 2, posts: 3 }];
  await sleep(Math.random() * 1000);
  return users.find((user) => user.id === userId).posts;
}

async function addPosts(userId) {
  const posts = await getPosts(userId);
  totalPosts += posts; // variable is read and immediately updated
}

await Promise.all([addPosts(1), addPosts(2)]);
console.log('Post count:', totalPosts);

max-nested-callbacks

Evita el infierno de las retrollamadas, evita retrollamadas profundamente anidadas:

/* eslint max-nested-callbacks: ["error", 3] */

// ❌
async1((err, result1) => {
  async2(result1, (err, result2) => {
    async3(result2, (err, result3) => {
      async4(result3, (err, result4) => {
        console.log(result4);
      });
    });
  });
});

// ✅
const result1 = await asyncPromise1();
const result2 = await asyncPromise2(result1);
const result3 = await asyncPromise3(result2);
const result4 = await asyncPromise4(result3);
console.log(result4);

El infierno de las llamadas de retorno dificulta la lectura y el mantenimiento del código. Se recomienda refactorizar las callbacks en Promises y utilizar la moderna sintaxis async/await.

no-return-await

No siempre es necesario escribir await cuando se devuelve un resultado asíncrono. Si necesitas esperar una "promesa" y devolverla inmediatamente, puede que no sea necesario.

// ❌
async () => {
  return await getUser(userId);
}

Todos los valores devueltos por una función async están envueltos en una Promise, por lo que puedes devolver directamente esa Promise.

// ✅
async () => {
  return getUser(userId);
}

Sin embargo, existe una excepción. Si hay un bloque try...catch alrededor de la función, eliminar el await le impedirá capturar la excepción. En este caso, se recomienda asignar explícitamente el resultado a una variable en una línea separada para aclarar la intención.

// 👎
async () => {
  try {
    return await getUser(userId);
  } catch (error) {
    // Handle getUser error
  }
}

// 👍
async () => {
  try {
    const user = await getUser(userId);
    return user;
  } catch (error) {
    // Handle getUser error
  }
}

prefer-promise-reject-errors

Se recomienda utilizar objetos Error cuando se rechacen promesas, ya que esto facilita el seguimiento de la pila de errores.

// ❌
Promise.reject('An error occurred');

// ✅
Promise.reject(new Error('An error occurred'));

node/handle-callback-err

Forzar el manejo de excepciones en callbacks asíncronos en Node.js.

// ❌
function callback(err, data) {
  console.log(data);
}

// ✅
function callback(err, data) {
  if (err) {
    console.log(err);
    return;
  }

  console.log(data);
}

En Node.js, las excepciones suelen pasarse como primer argumento a las funciones callback. Olvidar manejar estas excepciones puede llevar a problemas impredecibles en tu aplicación.

Esta regla se activa sólo cuando el primer parámetro de una función se llama err, pero también puedes personalizar el nombre del parámetro de excepción en el archivo .eslintrc.

node/no-sync

No se recomienda utilizar métodos síncronos en las APIs del núcleo de Node.js cuando existan alternativas asíncronas.

// ❌
const file = fs.readFileSync(path);

// ✅
const file = await fs.readFile(path);

Usar métodos síncronos para operaciones I/O en Node.js bloqueará el bucle de eventos. En la mayoría de los casos, usar métodos asíncronos para operaciones I/O es una mejor opción.

@typescript-eslint/await-thenable

No se recomienda await funciones o valores que no sean Promise.

// ❌
function getValue() {
  return someValue;
}

await getValue();

// ✅
async function getValue() {
  return someValue;
}

await getValue();

@typescript-eslint/no-floating-promises

Se recomienda adjuntar código de gestión de excepciones a las Promesas.

// ❌
myPromise()
  .then(() => {});

// ✅
myPromise()
  .then(() => {})
  .catch(() => {});

¡Desarrolle el hábito de manejar siempre las excepciones!

@typescript-eslint/no-misused-promises

No se recomienda pasar Promises a lugares donde no se espera que sean manejadas, como condiciones if.

// ❌
if (getUserFromDB()) {}

// ✅ 👎
if (await getUserFromDB()) {}

Es más recomendable extraer una variable para mejorar la legibilidad del código.

// ✅ 👍
const user = await getUserFromDB();
if (user) {}

Fuente