"Si un trabajador quiere hacer bien su trabajo, primero debe afilar sus herramientas." - Confucio, "Las Analectas de Confucio. Lu Linggong"
Página delantera > Programación > Comprender el infierno de las devoluciones de llamadas: el problema, las soluciones y los ejemplos de código

Comprender el infierno de las devoluciones de llamadas: el problema, las soluciones y los ejemplos de código

Publicado el 2024-11-08
Navegar:396

Understanding Callback Hell: The Problem, Solutions and Code Examples

El infierno de las devoluciones de llamadas también es un tema candente en las entrevistas técnicas, ya que pone a prueba la comprensión del desarrollador sobre el código asincrónico y su capacidad para refactorizar el código para mejorar su legibilidad y mantenibilidad.

Introducción

La programación asincrónica es crucial en el desarrollo moderno de JavaScript, ya que permite la ejecución sin bloqueo y mejora el rendimiento, especialmente para operaciones vinculadas a E/S. Sin embargo, esta conveniencia a veces puede llevar a una condición conocida como "infierno de devolución de llamadas".

En este artículo, profundizaremos en:

  1. Qué es el infierno de devolución de llamada y cómo surge.
  2. Los problemas que crea.
  3. Soluciones, incluido el uso de Promises y async/await.
  4. Ejemplos de código para que todo quede claro.

¿Qué es el infierno de devolución de llamada?

Infierno de devolución de llamada, a menudo denominado "Pirámide de la perdición", ocurre cuando múltiples operaciones asincrónicas anidadas dependen unas de otras para ejecutarse en secuencia. Este escenario conduce a una maraña de devoluciones de llamadas profundamente anidadas, lo que hace que el código sea difícil de leer, mantener y depurar.

Ejemplo de devolución de llamada infernal:

getData(function(data) {
  processData(data, function(processedData) {
    saveData(processedData, function(response) {
      sendNotification(response, function(notificationResult) {
        console.log("All done!");
      });
    });
  });
});

El código anterior realiza varias operaciones asincrónicas en secuencia. Si bien funciona, rápidamente se vuelve inmanejable si se agregan más tareas, lo que dificulta su comprensión y mantenimiento. La estructura anidada se asemeja a una pirámide, de ahí el término "Pirámide de la Perdición".

El problema del infierno de devolución de llamada

El infierno de las devoluciones de llamadas genera varios problemas:

  1. Difícil de mantener: el código profundamente anidado es difícil de modificar/ampliar. Podrías introducir errores simplemente intentando agregar nuevas funciones.
  2. Manejo de errores: el manejo adecuado de errores en diferentes capas anidadas se vuelve complejo. Tienes que manejar los errores para cada devolución de llamada individual, lo que puede generar código repetido.
  3. Legibilidad del código: comprender el flujo de ejecución se vuelve un desafío, especialmente para los desarrolladores que no están familiarizados con el código base.
  4. Escalabilidad: a medida que crece el número de devoluciones de llamadas anidadas, también aumenta la complejidad, lo que hace que el código no sea escalable y sea difícil de depurar.

Promesas: una solución al infierno de las devoluciones de llamadas

Para mitigar los problemas del infierno de devolución de llamadas, Promises se utilizan en JavaScript. Las promesas representan la eventual finalización (o falla) de una operación asincrónica y le permiten escribir código limpio y más manejable. Las promesas simplifican el código - Con las promesas, la estructura anidada se aplana y el manejo de errores está más centralizado, lo que hace que el código sea más fácil de leer y mantener.

Así es como se vería el ejemplo anterior del infierno de devolución de llamada usando Promesas:

getData()
 .then(data => processData(data))
 .then(processedData => saveData(processedData))
 .then(response => sendNotification(response))
 .then(notificationResult => {
 console.log("All done!");
 })
 .catch(error => {
 console.error("An error occurred:", error);
 });

Este enfoque elimina las devoluciones de llamadas profundamente anidadas. Cada bloque "luego" representa el siguiente paso en la cadena, lo que hace que el flujo sea mucho más lineal y más fácil de seguir. El manejo de errores también está centralizado en el bloque 'catch'.

Cómo funcionan las promesas

Las promesas tienen tres estados posibles:

  1. Pendiente: el estado inicial, lo que significa que la promesa aún no se ha cumplido o rechazado.
  2. Cumplido: La operación asincrónica se completó correctamente.
  3. Rechazado: La operación falló.

Un objeto Promise proporciona los métodos '.then()' y '.catch()' para manejar escenarios de éxito y fracaso.

function getData() {
 return new Promise((resolve, reject) => {
 // Simulating an async operation (e.g., API call)
 setTimeout(() => {
 const data = "Sample Data";
 resolve(data);
 }, 1000);
 });
}
getData()
 .then(data => {
 console.log("Data received:", data);
 })
 .catch(error => {
 console.error("Error fetching data:", error);
 });

En el código anterior, la función 'getData()' devuelve una Promesa. Si la operación asincrónica tiene éxito, la promesa se cumple con los datos, de lo contrario se rechaza con error.

Encadenando promesas

Una de las principales ventajas de las Promesas es que se pueden encadenar. Esto permite secuenciar operaciones asincrónicas sin anidar.

function fetchData() {
 return new Promise((resolve, reject) => {
 setTimeout(() => resolve("Data fetched"), 1000);
 });
}
function processData(data) {
 return new Promise((resolve, reject) => {
 setTimeout(() => resolve(`${data} and processed`), 1000);
 });
}
function saveData(data) {
 return new Promise((resolve, reject) => {
 setTimeout(() => resolve(`${data} and saved`), 1000);
 });
}
fetchData()
 .then(data => processData(data))
 .then(processedData => saveData(processedData))
 .then(result => {
 console.log(result); 
// Output => Data fetched and processed and saved
 })
 .catch(error => console.error("Error:", error));

Al encadenar Promesas, el código se vuelve plano, más legible y más fácil de mantener.

Async/Await: una alternativa aún mejor

Si bien las promesas son una mejora significativa con respecto a las devoluciones de llamada, aún pueden resultar engorrosas con cadenas extensas. Aquí es donde entra en juego async/await.
La sintaxis Async/await nos permite escribir código asincrónico de una manera que se asemeja al código sincrónico. Hace que su código sea más limpio y más fácil de razonar.

Usando Async/Await:

async function performOperations() {
  try {
    const data = await getData();
    const processedData = await processData(data);
    const response = await saveData(processedData);
    const notificationResult = await sendNotification(response);

    console.log("All done!");
  } catch (error) {
    console.error("Error:", error);
  }
}

performOperations();

En el código anterior:

  • La palabra clave 'async' se utiliza para definir una función asincrónica.
  • 'await' pausa la ejecución de la función hasta que la promesa se resuelva o rechace, lo que hace que el código parezca sincrónico.
  • El manejo de errores es mucho más sencillo, utilizando un único bloque 'try-catch'.
  • Async/await elimina el infierno de devolución de llamadas y las largas cadenas de promesas, lo que lo convierte en la forma preferida de manejar operaciones asincrónicas en JavaScript moderno.

Conclusión

Infierno de devolución de llamada es un problema común en JavaScript que surge cuando se trabaja con múltiples operaciones asincrónicas. Las devoluciones de llamadas profundamente anidadas generan código que no se puede mantener y es propenso a errores. Sin embargo, con la introducción de Promises y async/await, los desarrolladores ahora tienen formas de escribir código más limpio, más manejable y escalable.

Promises aplana las devoluciones de llamadas anidadas y centraliza el manejo de errores, mientras que async/await simplifica aún más la lógica asincrónica al hacerla parecer sincrónica. Ambas técnicas eliminan el caos del infierno de las devoluciones de llamadas y garantizan que su código siga siendo legible, incluso a medida que crece en complejidad.

Identificadores de redes sociales
Si este artículo te resultó útil, no dudes en conectarte conmigo en mis canales de redes sociales para obtener más información:

  • GitHub: [AmanjotSingh0908]
  • LinkedIn: [Amanjot Singh Saini]
  • Twitter: [@AmanjotSingh]

¡Gracias por leer!

Declaración de liberación Este artículo se reproduce en: https://dev.to/amanjotsingh/understanding-callback-hell-the-problem-solutions-and-code-examples-3loh?1 Si hay alguna infracción, comuníquese con [email protected] para borrarlo
Último tutorial Más>

Descargo de responsabilidad: Todos los recursos proporcionados provienen en parte de Internet. Si existe alguna infracción de sus derechos de autor u otros derechos e intereses, explique los motivos detallados y proporcione pruebas de los derechos de autor o derechos e intereses y luego envíelos al correo electrónico: [email protected]. Lo manejaremos por usted lo antes posible.

Copyright© 2022 湘ICP备2022001581号-3