"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 > Promesas en JavaScript: comprensión, manejo y dominio del código asíncrono

Promesas en JavaScript: comprensión, manejo y dominio del código asíncrono

Publicado el 2024-11-07
Navegar:883

Promises in JavaScript: Understanding, Handling, and Mastering Async Code

Introducción

Solía ​​​​trabajar como desarrollador de Java y recuerdo por primera vez cuando me puse en contacto con promesas en JavaScript. Aunque el concepto parecía simple, todavía no podía entender completamente cómo funcionaba Promises. Cambió cuando comencé a usarlos en proyectos y entendí los casos que resolvían. Luego llegó el momento AHA y todo quedó más claro. Con el tiempo, Promises se convirtió en un arma valiosa en mi cinturón de herramientas. Es extrañamente satisfactorio poder usarlos en el trabajo y resolver el manejo asíncrono entre funciones.

Probablemente te encuentres por primera vez con Promesas al obtener datos de una API, que también es el ejemplo más común. Recientemente me entrevistaron y adivinen cuál fue la primera pregunta: "¿Puede decirme la diferencia entre Promise y Async Await?". Lo agradezco porque lo veo como un buen punto de partida para saber mejor cómo entiende el solicitante cómo funcionan los mecanismos. Sin embargo, él o ella utiliza principalmente otras bibliotecas y marcos. Me permitió escribir las diferencias y describir buenas prácticas para manejar errores de funciones asíncronas.

¿Cuál es la promesa?

Comencemos con la pregunta inicial: “¿Cuál es la Promesa?” La promesa es un marcador de posición para el valor que aún no conocemos pero lo obtendremos como resultado de un cálculo/función asincrónica. Si la promesa sale bien, obtendremos el resultado. Si la promesa no sale bien, devolverá un error.

Un ejemplo básico de una promesa

Definiendo una promesa

Defines Promise llamando a su constructor y pasando dos funciones de devolución de llamada: resolve y reject.

const newPromise = new Promise((resolve, reject) => {
    resolve('Hello');
    // reject('Error');
});

Llamamos a la función de resolución cuando queremos resolver con éxito la Promesa. rechazar es para rechazar la promesa en el caso de que ocurra un error durante la evaluación de nuestra lógica.

Recuperando el resultado de la promesa

Luego usamos la función incorporada para obtener el resultado de la Promesa. Tiene dos devoluciones de llamada pasadas, resultado y error. El resultado se llama cuando la función resolver resuelve exitosamente la Promesa. Si la Promesa no se resuelve, se llama al segundo error de función. Esta función se activa por rechazo o por otro error que se genera.

newPromise.then(result => {
    console.log(result); // Hello
}, error => {
    console.log("There shouldn't be an error");
});

En nuestro ejemplo, obtendremos el resultado Hola porque resolvimos exitosamente la Promesa.

Manejo de errores de promesas.

Cuando se rechaza la Promesa, siempre se invoca su segunda devolución de llamada de error.

const newPromise1 = new Promise((resolve, reject) => {
  reject('An error occurred in Promise1');
});

newPromise1.then(
  (result) => {
    console.log(result); // It is not invoked
  },
  (error) => {
    console.log(error); // 'An error occurred in Promise1'
  }
);

Un enfoque más recomendado por su claridad es utilizar el método de captura integrado.

const newPromise2 = new Promise((resolve, reject) => {
  reject('An error occurred in Promise2');
});

newPromise2
  .then((result) => {
    console.log(result); // It is not invoked
  })
  .catch((error) => {
    console.log(error); // 'An error occurred in Promise2'
  });

el método catch está encadenado y ha proporcionado su propia devolución de llamada de error. Se invoca cuando se rechaza la Promesa.

Ambas versiones funcionan bien, pero en mi opinión el encadenamiento es más legible y es útil cuando se utilizan otros métodos integrados que cubrimos más adelante.

Encadenando promesas

El resultado de una promesa probablemente podría ser otra promesa. En ese caso, podemos encadenar un número arbitrario de funciones.

getJSON('categories.json')
    .then(categories => {
        console.log('Fetched categories:', categories);

        return getJSON(categories[0].itemsUrl);
    })
    .then(items => {
        console.log('Fetched items:', items);

        return getJSON(items[0].detailsUrl);
    })
    .then(details => {
        console.log('Fetched details:', details);
    })
    .catch(error => {
        console.error('An error has occurred:', error.message);
    });

En nuestro ejemplo, sirve para limitar los resultados de la búsqueda para obtener datos detallados. Cada función then también puede tener su devolución de llamada de error. Si solo nos importa detectar cualquier error en la cadena de llamadas, entonces podemos aprovechar la función catch. Se evaluará si alguna de las Promesas devuelve un error.

Promételo todo

A veces queremos esperar los resultados de promesas más independientes y luego actuar en consecuencia. Podemos usar la función incorporada Promise.all si no nos importa el orden en que se resolvieron las Promesas.

Promise.all([
    getJSON('categories.json'),
    getJSON('technology_items.json'),
    getJSON('science_items.json')
])
    .then(results => {
        const categories = results[0];
        const techItems = results[1];
        const scienceItems = results[2];

        console.log('Fetched categories:', categories);
        console.log('Fetched technology items:', techItems);
        console.log('Fetched science items:', scienceItems);

        // Fetch details of the first item in each category
        return Promise.all([
            getJSON(techItems[0].detailsUrl),
            getJSON(scienceItems[0].detailsUrl)
        ]);
    })
    .then(detailsResults => {
        const laptopDetails = detailsResults[0];
        const physicsDetails = detailsResults[1];

        console.log('Fetched laptop details:', laptopDetails);
        console.log('Fetched physics details:', physicsDetails);
    })
    .catch(error => {
        console.error('An error has occurred:', error.message);
    });

Promise.all toma una serie de promesas y devuelve una serie de resultados. Si se rechaza una de las Promesas, también se rechaza Promise.all.

Promesas de carreras

Otra funcionalidad incorporada es Promise.race. Se utiliza cuando tienes múltiples funciones asincrónicas (Promesas) y quieres competir con ellas.

Promise.race([
    getJSON('technology_items.json'),
    getJSON('science_items.json')
])
    .then(result => {
        console.log('First resolved data:', result);
    })
    .catch(error => {
        console.error('An error has occurred:', error.message);
    });

La ejecución de las Promesas puede tardar diferentes tiempos y Promise.race evalúa la primera Promesa resuelta o rechazada de la matriz. Se utiliza cuando no nos importa el orden pero queremos el resultado de la llamada asincrónica más rápida.

¿Qué es la espera asíncrona?

Como puede ver, escribir Promesas requiere una gran cantidad de código repetitivo. Afortunadamente, tenemos la función nativa Async Await, que facilita aún más el uso de Promises. Marcamos una función con la palabra async y con eso decimos que en algún lugar del código llamaremos a una función asincrónica y no debemos esperarla. Luego se llama a la función asíncrona con la palabra de espera.

Ejemplo básico de espera asíncrona

const fetchData = async () => {
    try {
        // Fetch the categories
        const categories = await getJSON('categories.json');
        console.log('Fetched categories:', categories);

        // Fetch items from the first category (Technology)
        const techItems = await getJSON(categories[0].itemsUrl);
        console.log('Fetched technology items:', techItems);

        // Fetch details of the first item in Technology (Laptops)
        const laptopDetails = await getJSON(techItems[0].detailsUrl);
        console.log('Fetched laptop details:', laptopDetails);
    } catch (error) {
        console.error('An error has occurred:', error.message);
    }
};

fetchData();

Nuestro fetchData está marcado como asíncrono y nos permite usar await para manejar llamadas asincrónicas dentro de la función. Llamamos a más Promesas y se evaluarán una tras otra.

Usamos el bloque try...catch si queremos manejar los errores. El error rechazado se captura en el bloque catch y podemos actuar en consecuencia como registrar el error.

¿Qué es diferente?

Ambas son características del manejo de JavaScript con código asincrónico. La principal diferencia está en la sintaxis cuando las promesas usan encadenamiento con then y catch, pero la sintaxis async await es más sincrónica. Hace que sea más fácil de leer. El manejo de errores para async await es más sencillo cuando aprovecha el bloque try...catch. Esta es una pregunta que puedes responder fácilmente en la entrevista. Durante la respuesta, podrás profundizar en la descripción de ambos y resaltar esas diferencias.

Funciones de promesa

Por supuesto, puedes usar todas las funciones con async await. Por ejemplo Promesa.todo.

const fetchAllData = async () => {
    try {
        // Use await with Promise.all to fetch multiple JSON files in parallel
        const [techItems, scienceItems, laptopDetails] = await Promise.all([
            getJSON('technology_items.json'),
            getJSON('science_items.json'),
            getJSON('laptops_details.json')
        ]);

        console.log('Fetched technology items:', techItems);
        console.log('Fetched science items:', scienceItems);
        console.log('Fetched laptop details:', laptopDetails);
    } catch (error) {
        console.error('An error occurred:', error.message);
    }
};

Casos de uso prácticos

Las promesas son una característica fundamental en JavaScript para manejar código asincrónico. Estas son las principales formas en que se utiliza:

Obteniendo datos de API

Como ya se mostró en los ejemplos anteriores, este es uno de los casos de uso más utilizados para Promises y se trabaja con él a diario.

Manejo de operaciones de archivos

La lectura y escritura de archivos de forma asincrónica se puede realizar mediante promesas, especialmente mediante el módulo fs.promises de Node.js

import * as fs from 'fs/promises';

const writeFileAsync = async (filePath, content, options = {}) => {
    try {
        await fs.writeFile(filePath, content, options);
        console.log(`File successfully written to ${filePath}`);
    } catch (error) {
        console.error(`Error writing file to ${filePath}:`, error.message);
    }
};

const filePath = 'output.txt';
const fileContent = 'Hello, this is some content to write to the file!';
const fileOptions = { encoding: 'utf8', flag: 'w' }; // Optional file write options

writeFileAsync(filePath, fileContent, fileOptions);

Bibliotecas basadas en promesas

Axios es una biblioteca con la que deberías estar familiarizado. Axios maneja solicitudes HTTP en el cliente y se utiliza ampliamente.

Express es un marco web para Node.js. Facilita la creación de aplicaciones web y API, y cuando usas promesas con Express, tu código permanece limpio y fácil de administrar.

Repositorio con ejemplos

Todos los ejemplos se pueden encontrar en: https://github.com/PrincAm/promise-example

Resumen

Las promesas son una parte fundamental de JavaScript, esenciales para manejar tareas asincrónicas en el desarrollo web. Ya sea para recuperar datos, trabajar con archivos o utilizar bibliotecas populares como Axios y Express, con frecuencia utilizarás promesas en tu código.

En este artículo, exploramos qué son las promesas, cómo definir y recuperar sus resultados y cómo manejar los errores de manera efectiva. También cubrimos funciones clave como encadenamiento, Promise.all y Promise.race. Finalmente, presentamos la sintaxis async await, que ofrece una forma más sencilla de trabajar con promesas.

Comprender estos conceptos es crucial para cualquier desarrollador de JavaScript, ya que son herramientas en las que confiará a diario.

Si aún no lo has probado, te recomiendo escribir un fragmento de código simple para obtener datos de una API. Puedes comenzar con una API divertida para experimentar. Además, todos los ejemplos y fragmentos de código están disponibles en este repositorio para que los explores.

Declaración de liberación Este artículo se reproduce en: https://dev.to/princam/promises-in-javascript-understanding-handling-and-mastering-async-code-10kn?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