En este tema, exploraremos patrones avanzados para manejar la programación asíncrona en TypeScript. Estos patrones te permitirán escribir código más eficiente y manejable cuando trabajes con operaciones asíncronas complejas.

Contenido

Introducción a los Patrones Asíncronos Avanzados

Los patrones asíncronos avanzados son técnicas que te permiten manejar múltiples operaciones asíncronas de manera eficiente. Estos patrones son esenciales cuando trabajas con aplicaciones que requieren múltiples llamadas a APIs, procesamiento de datos en paralelo, o cualquier otra tarea que implique operaciones asíncronas complejas.

Patrón de Promesas en Cadena

El patrón de promesas en cadena se utiliza para ejecutar una serie de operaciones asíncronas en secuencia. Cada operación se ejecuta solo después de que la anterior haya completado.

Ejemplo

function fetchData(url: string): Promise<string> {
    return new Promise((resolve, reject) => {
        setTimeout(() => {
            resolve(`Data from ${url}`);
        }, 1000);
    });
}

fetchData('https://api.example.com/data1')
    .then(data1 => {
        console.log(data1);
        return fetchData('https://api.example.com/data2');
    })
    .then(data2 => {
        console.log(data2);
        return fetchData('https://api.example.com/data3');
    })
    .then(data3 => {
        console.log(data3);
    })
    .catch(error => {
        console.error('Error fetching data:', error);
    });

Explicación

  1. fetchData es una función que simula una llamada a una API y devuelve una promesa.
  2. Las llamadas a fetchData se encadenan usando .then(), asegurando que cada llamada se ejecute solo después de que la anterior haya completado.
  3. Si alguna de las promesas falla, el error se captura en el bloque .catch().

Patrón de Paralelismo

El patrón de paralelismo se utiliza para ejecutar múltiples operaciones asíncronas en paralelo y esperar a que todas completen.

Ejemplo

function fetchData(url: string): Promise<string> {
    return new Promise((resolve, reject) => {
        setTimeout(() => {
            resolve(`Data from ${url}`);
        }, 1000);
    });
}

Promise.all([
    fetchData('https://api.example.com/data1'),
    fetchData('https://api.example.com/data2'),
    fetchData('https://api.example.com/data3')
])
.then(([data1, data2, data3]) => {
    console.log(data1);
    console.log(data2);
    console.log(data3);
})
.catch(error => {
    console.error('Error fetching data:', error);
});

Explicación

  1. Promise.all toma un array de promesas y devuelve una nueva promesa que se resuelve cuando todas las promesas en el array se han resuelto.
  2. Los resultados de las promesas se devuelven como un array en el mismo orden en que se pasaron a Promise.all.
  3. Si alguna de las promesas falla, el error se captura en el bloque .catch().

Patrón de Carrera

El patrón de carrera se utiliza para ejecutar múltiples operaciones asíncronas en paralelo y obtener el resultado de la primera que complete.

Ejemplo

function fetchData(url: string): Promise<string> {
    return new Promise((resolve, reject) => {
        setTimeout(() => {
            resolve(`Data from ${url}`);
        }, Math.random() * 2000);
    });
}

Promise.race([
    fetchData('https://api.example.com/data1'),
    fetchData('https://api.example.com/data2'),
    fetchData('https://api.example.com/data3')
])
.then(data => {
    console.log('First data received:', data);
})
.catch(error => {
    console.error('Error fetching data:', error);
});

Explicación

  1. Promise.race toma un array de promesas y devuelve una nueva promesa que se resuelve o rechaza tan pronto como una de las promesas en el array se resuelve o rechaza.
  2. En este ejemplo, la primera promesa que se resuelve proporciona el resultado.

Patrón de Secuencialización

El patrón de secuencialización se utiliza para ejecutar una serie de operaciones asíncronas en secuencia, pero con un enfoque más dinámico y flexible que el encadenamiento de promesas.

Ejemplo

async function fetchData(url: string): Promise<string> {
    return new Promise((resolve, reject) => {
        setTimeout(() => {
            resolve(`Data from ${url}`);
        }, 1000);
    });
}

async function fetchSequentially(urls: string[]): Promise<void> {
    for (const url of urls) {
        const data = await fetchData(url);
        console.log(data);
    }
}

const urls = [
    'https://api.example.com/data1',
    'https://api.example.com/data2',
    'https://api.example.com/data3'
];

fetchSequentially(urls)
    .then(() => {
        console.log('All data fetched sequentially');
    })
    .catch(error => {
        console.error('Error fetching data:', error);
    });

Explicación

  1. fetchSequentially es una función asíncrona que toma un array de URLs.
  2. Utiliza un bucle for...of para iterar sobre las URLs y await para esperar a que cada llamada a fetchData complete antes de continuar con la siguiente.
  3. Este enfoque es más flexible y legible que el encadenamiento de promesas.

Ejercicios Prácticos

Ejercicio 1: Promesas en Cadena

Escribe una función que realice tres llamadas a una API en secuencia y registre los resultados en la consola.

Ejercicio 2: Paralelismo

Escribe una función que realice tres llamadas a una API en paralelo y registre los resultados en la consola.

Ejercicio 3: Carrera

Escribe una función que realice tres llamadas a una API en paralelo y registre el resultado de la primera que complete.

Ejercicio 4: Secuencialización

Escribe una función que tome un array de URLs y realice llamadas a una API en secuencia, registrando cada resultado en la consola.

Soluciones

Solución Ejercicio 1

async function fetchSequentially(urls: string[]): Promise<void> {
    for (const url of urls) {
        const data = await fetchData(url);
        console.log(data);
    }
}

const urls1 = [
    'https://api.example.com/data1',
    'https://api.example.com/data2',
    'https://api.example.com/data3'
];

fetchSequentially(urls1);

Solución Ejercicio 2

Promise.all([
    fetchData('https://api.example.com/data1'),
    fetchData('https://api.example.com/data2'),
    fetchData('https://api.example.com/data3')
])
.then(([data1, data2, data3]) => {
    console.log(data1);
    console.log(data2);
    console.log(data3);
});

Solución Ejercicio 3

Promise.race([
    fetchData('https://api.example.com/data1'),
    fetchData('https://api.example.com/data2'),
    fetchData('https://api.example.com/data3')
])
.then(data => {
    console.log('First data received:', data);
});

Solución Ejercicio 4

async function fetchSequentially(urls: string[]): Promise<void> {
    for (const url of urls) {
        const data = await fetchData(url);
        console.log(data);
    }
}

const urls2 = [
    'https://api.example.com/data1',
    'https://api.example.com/data2',
    'https://api.example.com/data3'
];

fetchSequentially(urls2);

Conclusión

En esta sección, hemos explorado varios patrones avanzados para manejar la programación asíncrona en TypeScript. Estos patrones te permitirán escribir código más eficiente y manejable cuando trabajes con operaciones asíncronas complejas. Asegúrate de practicar estos patrones con los ejercicios proporcionados para reforzar tu comprensión. En el próximo módulo, nos adentraremos en herramientas y mejores prácticas para trabajar con TypeScript.

© Copyright 2024. Todos los derechos reservados