JavaScript

JavaScript

Tutorial JavaScript: Async / Await

JavaScript async await: manejo asincrónico. Aprende a manejar asincronía en JavaScript utilizando async y await con ejemplos detallados.

Aprende JavaScript y certifícate

Sintaxis declarativa: Transformación de código basado en promesas a estilo síncrono

La sintaxis async/await representa una evolución significativa en la forma de escribir código asíncrono en JavaScript. Esta característica, introducida en ES2017, permite estructurar operaciones asíncronas de manera que se lean y escriban como código síncrono tradicional, mejorando drásticamente la legibilidad y mantenibilidad del código.

Fundamentos de async/await

La palabra clave async se utiliza para declarar una función asíncrona, mientras que await permite esperar a que una promesa se resuelva y obtener su valor resultante. Lo revolucionario de esta sintaxis es que transforma visualmente el código basado en promesas en algo que parece código síncrono secuencial.

// Función que devuelve una promesa
function fetchUserData(userId) {
  return fetch(`https://api.example.com/users/${userId}`)
    .then(response => response.json());
}

// Transformación a async/await
async function getUserData(userId) {
  const response = await fetch(`https://api.example.com/users/${userId}`);
  const userData = await response.json();
  return userData;
}

Observa cómo la segunda versión elimina las cadenas de .then() y presenta un flujo lineal que refleja mejor la intención del código.

Reglas fundamentales

Existen algunas reglas importantes que debemos tener en cuenta:

  • La palabra clave await solo puede usarse dentro de funciones declaradas con async.
  • Una función async siempre devuelve una promesa, independientemente de lo que retorne explícitamente.
  • El valor que devuelve la expresión await es el valor resuelto de la promesa.
async function example() {
  return 123; // Se envuelve automáticamente en una promesa
}

// Equivalente a:
function example() {
  return Promise.resolve(123);
}

// Uso:
example().then(value => console.log(value)); // 123

Transformando cadenas de promesas

Una de las ventajas más evidentes de async/await es la simplificación de cadenas de promesas complejas. Veamos un ejemplo de transformación:

// Código basado en promesas
function getOrderDetails(userId) {
  return getUser(userId)
    .then(user => {
      return getOrders(user.id)
        .then(orders => {
          return getProductDetails(orders[0].productId)
            .then(product => {
              return { user, order: orders[0], product };
            });
        });
    });
}

// Transformado a async/await
async function getOrderDetails(userId) {
  const user = await getUser(userId);
  const orders = await getOrders(user.id);
  const product = await getProductDetails(orders[0].productId);
  return { user, order: orders[0], product };
}

La versión con async/await elimina la anidación excesiva y hace que el flujo de datos sea mucho más claro y fácil de seguir.

Variables y ámbito

Otra ventaja importante es el manejo de variables y ámbito. Con promesas encadenadas, a menudo necesitamos declarar variables en un ámbito superior para compartirlas entre diferentes callbacks:

// Con promesas
function processUserData(userId) {
  let userData;
  return fetchUserData(userId)
    .then(data => {
      userData = data;
      return validateUser(userData);
    })
    .then(isValid => {
      if (isValid) {
        return transformUserData(userData);
      }
      throw new Error('Invalid user');
    });
}

// Con async/await
async function processUserData(userId) {
  const userData = await fetchUserData(userId);
  const isValid = await validateUser(userData);
  
  if (!isValid) {
    throw new Error('Invalid user');
  }
  
  return transformUserData(userData);
}

Con async/await, las variables siguen el flujo natural del código, lo que reduce la complejidad y posibles errores.

Operaciones en paralelo

Aunque async/await favorece un estilo secuencial, también podemos ejecutar operaciones en paralelo cuando sea necesario:

// Ejecución secuencial (más lenta)
async function getResourcesSequential() {
  const users = await fetchUsers();
  const products = await fetchProducts();
  return { users, products };
}

// Ejecución en paralelo (más eficiente)
async function getResourcesParallel() {
  const usersPromise = fetchUsers();
  const productsPromise = fetchProducts();
  
  const users = await usersPromise;
  const products = await productsPromise;
  
  return { users, products };
}

También podemos utilizar Promise.all() para una sintaxis más concisa:

async function getResourcesParallel() {
  const [users, products] = await Promise.all([
    fetchUsers(),
    fetchProducts()
  ]);
  
  return { users, products };
}

Transformando callbacks a async/await

Muchas APIs antiguas de JavaScript utilizan callbacks. Podemos transformarlas en promesas y luego usar async/await:

// Función basada en callbacks
function readFile(path, callback) {
  fs.readFile(path, 'utf8', (err, data) => {
    if (err) {
      callback(err);
      return;
    }
    callback(null, data);
  });
}

// Promisificación
function readFilePromise(path) {
  return new Promise((resolve, reject) => {
    fs.readFile(path, 'utf8', (err, data) => {
      if (err) reject(err);
      else resolve(data);
    });
  });
}

// Uso con async/await
async function processFile() {
  try {
    const content = await readFilePromise('./data.json');
    return JSON.parse(content);
  } catch (error) {
    console.error('Error processing file:', error);
    throw error;
  }
}

Esta transformación progresiva permite modernizar gradualmente bases de código antiguas.

Consideraciones de rendimiento

Es importante entender que async/await es azúcar sintáctico sobre promesas. No mejora el rendimiento por sí mismo, pero puede hacer que el código sea más mantenible y menos propenso a errores. El motor de JavaScript sigue utilizando promesas internamente.

// Estas dos funciones son equivalentes en rendimiento
async function fetchData() {
  return await fetch('https://api.example.com/data');
}

function fetchData() {
  return fetch('https://api.example.com/data');
}

En el primer ejemplo, el await es innecesario ya que estamos simplemente devolviendo la promesa. Es importante usar async/await donde realmente aporta claridad y no por mera convención.

Manejo de errores: Implementación de try/catch en funciones asíncronas

El manejo de errores es un aspecto crucial en la programación asíncrona con JavaScript. Cuando trabajamos con async/await, podemos utilizar la estructura tradicional try/catch para capturar y gestionar excepciones, lo que nos permite escribir código más robusto y predecible.

Fundamentos del manejo de errores en async/await

A diferencia de las promesas donde utilizamos el método .catch(), con async/await podemos emplear la sintaxis try/catch convencional. Esto nos permite unificar el manejo de errores tanto para operaciones síncronas como asíncronas dentro de una misma función.

async function getUserData(userId) {
  try {
    const response = await fetch(`https://api.example.com/users/${userId}`);
    
    if (!response.ok) {
      throw new Error(`HTTP error! Status: ${response.status}`);
    }
    
    const userData = await response.json();
    return userData;
  } catch (error) {
    console.error('Error fetching user data:', error);
    throw error; // Re-lanzamos el error para manejarlo en niveles superiores
  }
}

En este ejemplo, el bloque try/catch captura cualquier error que pueda ocurrir durante la petición fetch o al procesar la respuesta JSON.

Captura de diferentes tipos de errores

Podemos distinguir entre diferentes tipos de errores para proporcionar un manejo más específico según la naturaleza del problema:

async function processUserData(userId) {
  try {
    const userData = await getUserData(userId);
    const processedData = await processData(userData);
    return processedData;
  } catch (error) {
    if (error instanceof NetworkError) {
      // Manejo específico para errores de red
      console.error('Network problem detected:', error.message);
      return { error: 'connectivity', message: 'Please check your connection' };
    } else if (error instanceof ValidationError) {
      // Manejo específico para errores de validación
      console.error('Validation failed:', error.message);
      return { error: 'validation', message: 'Invalid user data' };
    } else {
      // Errores desconocidos
      console.error('Unknown error:', error);
      return { error: 'unknown', message: 'An unexpected error occurred' };
    }
  }
}

Esta técnica nos permite proporcionar respuestas personalizadas según el tipo de error encontrado.

Errores en operaciones paralelas

Cuando ejecutamos múltiples operaciones asíncronas en paralelo con Promise.all(), un error en cualquiera de ellas provocará que toda la operación falle. Podemos manejar esto con try/catch:

async function fetchMultipleResources() {
  try {
    const [users, products, orders] = await Promise.all([
      fetchUsers(),
      fetchProducts(),
      fetchOrders()
    ]);
    
    return { users, products, orders };
  } catch (error) {
    console.error('One of the requests failed:', error);
    // Podemos intentar una estrategia alternativa
    return await fetchResourcesSequentially();
  }
}

Si necesitamos que los errores individuales no afecten al conjunto, podemos usar Promise.allSettled():

async function fetchResourcesSafely() {
  try {
    const results = await Promise.allSettled([
      fetchUsers(),
      fetchProducts(),
      fetchOrders()
    ]);
    
    return results.map(result => {
      if (result.status === 'fulfilled') {
        return result.value;
      } else {
        console.warn('Operation failed:', result.reason);
        return null; // O un valor por defecto apropiado
      }
    });
  } catch (error) {
    // Este bloque solo se ejecutará si hay un error en Promise.allSettled mismo
    console.error('Critical error in batch operation:', error);
    throw error;
  }
}

Propagación controlada de errores

A veces queremos propagar errores a niveles superiores de forma controlada. Podemos hacerlo relanzando el error después de registrarlo o realizar alguna acción:

async function validateUserAccess(userId, resourceId) {
  try {
    const user = await getUser(userId);
    const hasAccess = await checkAccess(user, resourceId);
    
    if (!hasAccess) {
      throw new AccessDeniedError(`User ${userId} cannot access resource ${resourceId}`);
    }
    
    return true;
  } catch (error) {
    // Registramos el error para auditoría
    await logSecurityEvent({
      userId,
      resourceId,
      error: error.message,
      timestamp: new Date()
    });
    
    // Relanzamos para que el llamador lo maneje
    throw error;
  }
}

Errores en funciones asíncronas autoejecutadas

Cuando usamos funciones asíncronas autoejecutadas (IIFE), debemos recordar que los errores no capturados se convertirán en promesas rechazadas no manejadas:

// Incorrecto: error no manejado
(async function() {
  await riskyOperation(); // Si falla, tendremos una promesa rechazada no manejada
})();

// Correcto: con manejo de errores
(async function() {
  try {
    await riskyOperation();
  } catch (error) {
    console.error('Operation failed:', error);
  }
})();

// Alternativa: usando .catch() en la promesa devuelta
(async function() {
  await riskyOperation();
})().catch(error => {
  console.error('Operation failed:', error);
});

Patrones avanzados de manejo de errores

Retry pattern (patrón de reintento)

Podemos implementar un patrón de reintento para operaciones que pueden fallar temporalmente:

async function fetchWithRetry(url, options = {}, maxRetries = 3) {
  let lastError;
  
  for (let attempt = 0; attempt < maxRetries; attempt++) {
    try {
      const response = await fetch(url, options);
      if (!response.ok) throw new Error(`HTTP error! Status: ${response.status}`);
      return await response.json();
    } catch (error) {
      console.warn(`Attempt ${attempt + 1} failed:`, error);
      lastError = error;
      
      // Espera exponencial entre reintentos
      if (attempt < maxRetries - 1) {
        const delay = Math.pow(2, attempt) * 1000;
        await new Promise(resolve => setTimeout(resolve, delay));
      }
    }
  }
  
  throw new Error(`All ${maxRetries} attempts failed. Last error: ${lastError.message}`);
}

Timeout pattern (patrón de tiempo de espera)

Podemos establecer un tiempo máximo de espera para operaciones asíncronas:

async function fetchWithTimeout(url, options = {}, timeoutMs = 5000) {
  const controller = new AbortController();
  const timeoutId = setTimeout(() => controller.abort(), timeoutMs);
  
  try {
    const response = await fetch(url, {
      ...options,
      signal: controller.signal
    });
    
    clearTimeout(timeoutId);
    
    if (!response.ok) {
      throw new Error(`HTTP error! Status: ${response.status}`);
    }
    
    return await response.json();
  } catch (error) {
    clearTimeout(timeoutId);
    
    if (error.name === 'AbortError') {
      throw new Error(`Request timed out after ${timeoutMs}ms`);
    }
    
    throw error;
  }
}

Errores en funciones asíncronas dentro de bucles

El manejo de errores en bucles asíncronos requiere especial atención:

async function processItems(items) {
  const results = [];
  const errors = [];
  
  for (const item of items) {
    try {
      const result = await processItem(item);
      results.push(result);
    } catch (error) {
      console.error(`Error processing item ${item.id}:`, error);
      errors.push({ item, error: error.message });
    }
  }
  
  return { results, errors };
}

Este patrón nos permite continuar procesando el resto de los elementos incluso si algunos fallan, recopilando tanto resultados exitosos como errores.

Errores en constructores de clases asíncronas

JavaScript no permite que los constructores sean asíncronos. Para clases que requieren inicialización asíncrona, podemos usar un patrón de fábrica asíncrona:

class DataProcessor {
  constructor(data) {
    this.data = data;
    this.isInitialized = false;
  }
  
  async initialize() {
    try {
      this.schema = await fetchSchema();
      this.validator = new Validator(this.schema);
      this.isInitialized = true;
    } catch (error) {
      throw new Error(`Initialization failed: ${error.message}`);
    }
  }
  
  // Método de fábrica estático
  static async create(data) {
    const processor = new DataProcessor(data);
    await processor.initialize();
    return processor;
  }
  
  async process() {
    if (!this.isInitialized) {
      throw new Error('DataProcessor not initialized');
    }
    
    // Procesamiento con manejo de errores
    try {
      return await this.validator.validate(this.data);
    } catch (error) {
      throw new Error(`Validation error: ${error.message}`);
    }
  }
}

// Uso
try {
  const processor = await DataProcessor.create(inputData);
  const result = await processor.process();
  console.log('Processing successful:', result);
} catch (error) {
  console.error('Processing failed:', error);
}

Este patrón garantiza que el objeto esté completamente inicializado antes de usarlo, con un manejo de errores adecuado durante la inicialización.

Patrones avanzados: Loops asíncronos, ejecución paralela y secuencial controlada

Los patrones avanzados de async/await nos permiten gestionar flujos de ejecución complejos que van más allá de simples operaciones secuenciales. Estos patrones son fundamentales para desarrollar aplicaciones JavaScript robustas que manejen eficientemente operaciones asíncronas múltiples, ya sea en paralelo o en secuencia controlada.

Loops asíncronos

Trabajar con bucles y operaciones asíncronas puede ser complicado si no se aplican los patrones correctos. Veamos diferentes enfoques para manejar colecciones de datos de forma asíncrona:

Iteración secuencial con for...of

El bucle for...of combinado con await permite procesar elementos secuencialmente, esperando a que cada operación termine antes de iniciar la siguiente:

async function processItemsSequentially(items) {
  const results = [];
  
  for (const item of items) {
    // Cada iteración espera a que termine la anterior
    const result = await processItem(item);
    results.push(result);
  }
  
  return results;
}

Este patrón es útil cuando el orden de procesamiento es importante o cuando necesitamos limitar la carga en recursos externos.

Iteración paralela controlada

Para procesar elementos en paralelo pero con un límite de concurrencia, podemos implementar un patrón de "pool" de promesas:

async function processWithConcurrencyLimit(items, concurrencyLimit = 3) {
  const results = [];
  const running = new Set();
  
  for (const item of items) {
    const promise = processItem(item).then(result => {
      running.delete(promise);
      return result;
    });
    
    running.add(promise);
    results.push(promise);
    
    if (running.size >= concurrencyLimit) {
      // Espera a que termine al menos una tarea antes de continuar
      await Promise.race(running);
    }
  }
  
  // Espera a que terminen todas las tareas pendientes
  return Promise.all(results);
}

Este patrón es ideal para balancear velocidad y carga, permitiendo paralelismo controlado.

Procesamiento por lotes (batching)

Cuando trabajamos con grandes colecciones, procesar los elementos en lotes puede mejorar significativamente el rendimiento:

async function processBatches(items, batchSize = 5) {
  const results = [];
  
  for (let i = 0; i < items.length; i += batchSize) {
    const batch = items.slice(i, i + batchSize);
    // Procesa cada lote en paralelo internamente
    const batchResults = await Promise.all(
      batch.map(item => processItem(item))
    );
    results.push(...batchResults);
  }
  
  return results;
}

Este enfoque combina las ventajas de la ejecución secuencial y paralela, procesando lotes en secuencia pero los elementos dentro de cada lote en paralelo.

Ejecución paralela con control de errores

La ejecución paralela nos permite maximizar el rendimiento, pero necesitamos estrategias para manejar fallos parciales:

Promise.all con manejo de errores mejorado

Podemos mejorar Promise.all para que no falle completamente si una promesa es rechazada:

async function safeParallel(promises) {
  const results = await Promise.allSettled(promises);
  
  return results.map((result, index) => {
    if (result.status === 'fulfilled') {
      return { success: true, value: result.value, index };
    } else {
      return { success: false, error: result.reason, index };
    }
  });
}

// Uso
const operations = [fetchUsers(), fetchProducts(), fetchOrders()];
const results = await safeParallel(operations);

// Procesar resultados y errores
const successfulResults = results.filter(r => r.success).map(r => r.value);
const failedOperations = results.filter(r => !r.success);

Este patrón nos permite continuar con los resultados exitosos incluso cuando algunas operaciones fallan.

Ejecución paralela con timeout

Para evitar que operaciones lentas bloqueen todo el proceso, podemos implementar un timeout individual para cada promesa:

function withTimeout(promise, timeoutMs = 5000) {
  let timeoutId;
  
  const timeoutPromise = new Promise((_, reject) => {
    timeoutId = setTimeout(() => {
      reject(new Error(`Operation timed out after ${timeoutMs}ms`));
    }, timeoutMs);
  });
  
  return Promise.race([
    promise,
    timeoutPromise
  ]).finally(() => clearTimeout(timeoutId));
}

// Uso en paralelo
async function fetchDataWithTimeouts(urls) {
  return Promise.all(
    urls.map(url => withTimeout(fetch(url).then(r => r.json()), 3000))
  );
}

Este patrón garantiza que las operaciones no se extiendan indefinidamente, estableciendo límites de tiempo claros.

Ejecución secuencial controlada

La ejecución secuencial nos permite controlar el flujo de operaciones con mayor precisión:

Cadena de promesas dinámica

Podemos construir una cadena de promesas dinámicamente a partir de un array de tareas:

async function executeSequentially(tasks) {
  return tasks.reduce(async (previousPromise, task) => {
    // Espera a que termine la tarea anterior
    const previousResult = await previousPromise;
    
    // Ejecuta la tarea actual con el resultado anterior
    const currentResult = await task(previousResult);
    
    return currentResult;
  }, Promise.resolve(null)); // Valor inicial
}

// Ejemplo de uso
const tasks = [
  () => fetchUserProfile(userId),
  userProfile => fetchUserPermissions(userProfile.id),
  permissions => validateAccess(permissions, resourceId)
];

const accessResult = await executeSequentially(tasks);

Este patrón es útil para flujos de trabajo donde cada paso depende del resultado del anterior.

Ejecución secuencial con control de flujo

Podemos implementar un patrón que permita decisiones condicionales entre pasos:

async function workflowEngine(initialData) {
  let state = { ...initialData, errors: [] };
  
  // Paso 1: Validación
  try {
    state = await validateUserData(state);
    if (!state.isValid) {
      return { ...state, status: 'validation_failed' };
    }
  } catch (error) {
    state.errors.push({ step: 'validation', error: error.message });
    return { ...state, status: 'error' };
  }
  
  // Paso 2: Procesamiento
  try {
    state = await processUserData(state);
  } catch (error) {
    state.errors.push({ step: 'processing', error: error.message });
    // Continuamos al siguiente paso a pesar del error
  }
  
  // Paso 3: Almacenamiento
  try {
    state = await saveUserData(state);
    return { ...state, status: 'success' };
  } catch (error) {
    state.errors.push({ step: 'storage', error: error.message });
    return { ...state, status: 'partial_success' };
  }
}

Este patrón implementa un flujo de trabajo con decisiones basadas en resultados intermedios y manejo de errores específico para cada paso.

Patrones de cancelación

La capacidad de cancelar operaciones asíncronas es crucial en aplicaciones interactivas:

async function fetchWithCancellation(url) {
  const controller = new AbortController();
  const { signal } = controller;
  
  // Expone el método de cancelación
  const promise = fetch(url, { signal }).then(r => r.json());
  promise.cancel = () => controller.abort();
  
  return promise;
}

// Uso
const dataPromise = fetchWithCancellation('https://api.example.com/data');

// En algún evento (por ejemplo, el usuario navega a otra página)
dataPromise.cancel();

Este patrón nos permite detener operaciones en curso cuando ya no son necesarias, ahorrando recursos y evitando actualizaciones de estado innecesarias.

Sincronización y exclusión mutua

En algunos casos, necesitamos garantizar que ciertas operaciones asíncronas no se ejecuten simultáneamente:

class AsyncMutex {
  constructor() {
    this.locked = false;
    this.waitingQueue = [];
  }
  
  async acquire() {
    if (!this.locked) {
      this.locked = true;
      return;
    }
    
    // Si ya está bloqueado, espera en la cola
    return new Promise(resolve => {
      this.waitingQueue.push(resolve);
    });
  }
  
  release() {
    if (this.waitingQueue.length > 0) {
      // Desbloquea el siguiente en la cola
      const nextResolve = this.waitingQueue.shift();
      nextResolve();
    } else {
      this.locked = false;
    }
  }
  
  // Método de utilidad para ejecutar código con exclusión mutua
  async withLock(fn) {
    await this.acquire();
    try {
      return await fn();
    } finally {
      this.release();
    }
  }
}

// Ejemplo: Garantizar acceso exclusivo a un recurso
const cacheMutex = new AsyncMutex();

async function getOrFetchData(key) {
  return cacheMutex.withLock(async () => {
    // Verificar caché
    if (cache.has(key)) {
      return cache.get(key);
    }
    
    // Obtener datos y actualizar caché
    const data = await fetchData(key);
    cache.set(key, data);
    return data;
  });
}

Este patrón es útil para sincronizar acceso a recursos compartidos como caché, almacenamiento local o cualquier estado que requiera operaciones atómicas.

Composición de patrones

Los patrones más potentes surgen de la combinación de técnicas básicas. Por ejemplo, podemos crear un sistema de tareas en segundo plano con prioridades:

class TaskQueue {
  constructor() {
    this.highPriority = [];
    this.normalPriority = [];
    this.lowPriority = [];
    this.running = false;
  }
  
  addTask(task, priority = 'normal') {
    switch (priority) {
      case 'high': this.highPriority.push(task); break;
      case 'low': this.lowPriority.push(task); break;
      default: this.normalPriority.push(task);
    }
    
    if (!this.running) {
      this.processQueue();
    }
  }
  
  async processQueue() {
    this.running = true;
    
    try {
      while (
        this.highPriority.length || 
        this.normalPriority.length || 
        this.lowPriority.length
      ) {
        // Procesar primero tareas de alta prioridad
        const task = this.highPriority.shift() || 
                     this.normalPriority.shift() || 
                     this.lowPriority.shift();
        
        await task();
      }
    } finally {
      this.running = false;
    }
  }
}

Este patrón combina colas de prioridad con ejecución secuencial para gestionar tareas en segundo plano de manera eficiente.

Los patrones avanzados de async/await nos permiten construir sistemas asíncronos sofisticados y robustos, adaptados a las necesidades específicas de nuestras aplicaciones. La clave está en entender las ventajas y limitaciones de cada enfoque para aplicar el patrón más adecuado a cada situación.

Aprende JavaScript online

Otros ejercicios de programación de JavaScript

Evalúa tus conocimientos de esta lección Async / Await con nuestros retos de programación de tipo Test, Puzzle, Código y Proyecto con VSCode, guiados por IA.

Clases y objetos

JavaScript
Código

Uso de operadores

JavaScript
Puzzle

Uso de operadores

JavaScript
Test

Estructuras de control

JavaScript
Test

Proyecto Manipulación DOM

JavaScript
Proyecto

Excepciones

JavaScript
Test

Transformación con map()

JavaScript
Código

Arrays y Métodos

JavaScript
Código

Reto Métodos de Strings

JavaScript
Código

Transformación con map()

JavaScript
Puzzle

Funciones flecha

JavaScript
Test

Async / Await

JavaScript
Código

Selección de elementos DOM

JavaScript
Puzzle

API Fetch

JavaScript
Código

Encapsulación

JavaScript
Test

Mapas con Map

JavaScript
Código

Creación y uso de variables

JavaScript
Puzzle

Polimorfismo

JavaScript
Puzzle

Reto Funciones flecha

JavaScript
Código

Tipos de datos

JavaScript
Puzzle

Reto Operadores avanzados

JavaScript
Código

Promises

JavaScript
Código

Reto Estructuras de control

JavaScript
Código

Estructuras de control

JavaScript
Puzzle

Pruebas unitarias

JavaScript
Proyecto

Inmutabilidad y programación funcional pura

JavaScript
Código

Funciones flecha

JavaScript
Puzzle

Polimorfismo

JavaScript
Test

Reto Polimorfismo

JavaScript
Código

Array

JavaScript
Código

Transformación con map()

JavaScript
Test

Reto Variables

JavaScript
Código

Gestor de tareas con JavaScript

JavaScript
Proyecto

Proyecto Modificación de elementos DOM

JavaScript
Proyecto

Manipulación DOM

JavaScript
Test

Funciones

JavaScript
Test

Conjuntos con Set

JavaScript
Código

Reto Prototipos y cadena de prototipos

JavaScript
Código

Reto Encapsulación

JavaScript
Código

Funciones flecha

JavaScript
Código

Async / Await

JavaScript
Código

Reto Excepciones

JavaScript
Código

Reto Filtrado con filter() y find()

JavaScript
Código

Creación y uso de variables

JavaScript
Test

Excepciones

JavaScript
Puzzle

Promises

JavaScript
Código

Funciones cierre (closure)

JavaScript
Test

Reto Herencia

JavaScript
Código

Herencia

JavaScript
Puzzle

Proyecto Eventos del DOM

JavaScript
Proyecto

Herencia

JavaScript
Test

Selección de elementos DOM

JavaScript
Test

Modificación de elementos DOM

JavaScript
Test

Reto Clases y objetos

JavaScript
Código

Filtrado con filter() y find()

JavaScript
Test

Funciones cierre (closure)

JavaScript
Puzzle

Reto Destructuring de objetos y arrays

JavaScript
Código

Callbacks

JavaScript
Código

Funciones

JavaScript
Puzzle

Mapas con Map

JavaScript
Test

Reducción con reduce()

JavaScript
Test

Callbacks

JavaScript
Puzzle

Manipulación DOM

JavaScript
Puzzle

Introducción al DOM

JavaScript
Proyecto

Reto Funciones

JavaScript
Código

Reto Funciones cierre (closure)

JavaScript
Código

Promises

JavaScript
Test

Reto Reducción con reduce()

JavaScript
Código

Async / Await

JavaScript
Test

Reto Estructuras de control

JavaScript
Código

Eventos del DOM

JavaScript
Puzzle

Introducción a JavaScript

JavaScript
Puzzle

Async / Await

JavaScript
Puzzle

Promises

JavaScript
Puzzle

Selección de elementos DOM

JavaScript
Proyecto

Filtrado con filter() y find()

JavaScript
Código

Callbacks

JavaScript
Test

Creación de clases y objetos Restaurante

JavaScript
Código

Reducción con reduce()

JavaScript
Código

Filtrado con filter() y find()

JavaScript
Puzzle

Reducción con reduce()

JavaScript
Puzzle

Conjuntos con Set

JavaScript
Puzzle

Herencia de clases

JavaScript
Código

Eventos del DOM

JavaScript
Test

Clases y objetos

JavaScript
Puzzle

Modificación de elementos DOM

JavaScript
Puzzle

Mapas con Map

JavaScript
Puzzle

Proyecto carrito compra agoodshop

JavaScript
Proyecto

Introducción a JavaScript

JavaScript
Test

Reto Mapas con Map

JavaScript
Código

Funciones

JavaScript
Código

Proyecto administrador de contactos

JavaScript
Proyecto

Reto Expresiones regulares

JavaScript
Código

Tipos de datos

JavaScript
Test

Clases y objetos

JavaScript
Test

Array

JavaScript
Test

Conjuntos con Set

JavaScript
Test

Array

JavaScript
Puzzle

Encapsulación

JavaScript
Puzzle

Todas las lecciones de JavaScript

Accede a todas las lecciones de JavaScript y aprende con ejemplos prácticos de código y ejercicios de programación con IDE web sin instalar nada.

Introducción A Javascript

JavaScript

Introducción Y Entorno

Tipos De Datos

JavaScript

Sintaxis

Variables

JavaScript

Sintaxis

Operadores

JavaScript

Sintaxis

Estructuras De Control

JavaScript

Sintaxis

Funciones

JavaScript

Sintaxis

Funciones Cierre (Closure)

JavaScript

Sintaxis

Métodos De Strings

JavaScript

Sintaxis

Funciones Cierre (Closure)

JavaScript

Sintaxis

Operadores Avanzados

JavaScript

Sintaxis

Funciones

JavaScript

Sintaxis

Expresiones Regulares

JavaScript

Sintaxis

Estructuras De Control

JavaScript

Sintaxis

Arrays Y Métodos

JavaScript

Estructuras De Datos

Conjuntos Con Set

JavaScript

Estructuras De Datos

Mapas Con Map

JavaScript

Estructuras De Datos

Conjuntos Con Set

JavaScript

Estructuras De Datos

Funciones Flecha

JavaScript

Programación Funcional

Filtrado Con Filter() Y Find()

JavaScript

Programación Funcional

Transformación Con Map()

JavaScript

Programación Funcional

Reducción Con Reduce()

JavaScript

Programación Funcional

Funciones Flecha

JavaScript

Programación Funcional

Transformación Con Map()

JavaScript

Programación Funcional

Inmutabilidad Y Programación Funcional Pura

JavaScript

Programación Funcional

Clases Y Objetos

JavaScript

Programación Orientada A Objetos

Excepciones

JavaScript

Programación Orientada A Objetos

Encapsulación

JavaScript

Programación Orientada A Objetos

Herencia

JavaScript

Programación Orientada A Objetos

Polimorfismo

JavaScript

Programación Orientada A Objetos

This Y Contexto

JavaScript

Programación Orientada A Objetos

Patrón De Módulos Y Namespace

JavaScript

Programación Orientada A Objetos

Prototipos Y Cadena De Prototipos

JavaScript

Programación Orientada A Objetos

Destructuring De Objetos Y Arrays

JavaScript

Programación Orientada A Objetos

Manipulación Dom

JavaScript

Dom

Selección De Elementos Dom

JavaScript

Dom

Modificación De Elementos Dom

JavaScript

Dom

Eventos Del Dom

JavaScript

Dom

Localstorage Y Sessionstorage

JavaScript

Dom

Bom (Browser Object Model)

JavaScript

Dom

Callbacks

JavaScript

Programación Asíncrona

Promises

JavaScript

Programación Asíncrona

Async / Await

JavaScript

Programación Asíncrona

Promises

JavaScript

Programación Asíncrona

Api Fetch

JavaScript

Programación Asíncrona

Async / Await

JavaScript

Programación Asíncrona

Naturaleza De Js Y Event Loop

JavaScript

Programación Asíncrona

Callbacks

JavaScript

Programación Asíncrona

Websockets

JavaScript

Programación Asíncrona

Módulos En Es6

JavaScript

Construcción

Configuración De Bundlers Como Vite

JavaScript

Construcción

Eslint Y Calidad De Código

JavaScript

Construcción

Npm Y Dependencias

JavaScript

Construcción

Introducción A Pruebas En Js

JavaScript

Testing

Pruebas Unitarias

JavaScript

Testing

Accede GRATIS a JavaScript y certifícate

En esta lección

Objetivos de aprendizaje de esta lección

  1. Comprender el concepto de funciones asíncronas (async) en JavaScript y su relación con las promesas.
  2. Aprender a utilizar la palabra clave async para declarar funciones asíncronas.
  3. Entender cómo el valor retornado dentro de una función async se envuelve automáticamente en una promesa.
  4. Conocer el operador await y cómo se utiliza para pausar la ejecución hasta que una promesa se resuelva o rechace.
  5. Aprender a manejar errores utilizando try/catch en funciones async.
  6. Comprender cómo async y await simplifican y mejoran la legibilidad del código en situaciones con operaciones asíncronas.