¿Por qué los generadores comunes permiten escribir código asíncrono?
sobes.tech AI
Respuesta de la IA
Los generadores comunes en JavaScript permiten escribir código asíncrono gracias a su capacidad para pausar y reanudar la ejecución, así como para transmitir valores en ambas direcciones (a través de yield y next()).
Así es como funciona en el contexto de la asincronía:
-
Pausar la ejecución (
yield): Cuando se alcanza la instrucciónyield, el generador se pausa y devuelve un valor. En código asíncrono, este valor puede ser una promesa u otra "acción" que debe realizarse de forma asíncrona. -
Esperar el resultado asíncrono: El código externo (por ejemplo, un "runner" o envoltorio para el generador) recibe el valor pasado a través de
yield. Si es una promesa, espera a que se resuelva o rechace. -
Reanudar la ejecución (
next()): Tras completar la operación asíncrona (resolución de la promesa), el resultado asíncrono (el valor de la promesa) se pasa de nuevo al generador mediante el métodonext(). El generador reanuda la ejecución desde donde fue pausado. -
Transmitir el resultado: El valor pasado a
next()se convierte en el resultado de la expresiónyielddentro del generador. De este modo, el generador puede "obtener" el resultado de la operación asíncrona y continuar trabajando con él, como si el código fuera sincrónico.
Este patrón crea una ilusión de ejecución sincrónica para operaciones asíncronas, haciendo que el código sea más legible y manejable en comparación con el uso tradicional de callbacks o cadenas complejas de .then().
Un ejemplo de esto son los envoltorios como co (aunque ahora son más comunes async/await, que en esencia son azúcar sintáctico sobre generadores y promesas):
// Ejemplo de uso de generador para asincronía
function* myAsyncGenerator() {
try {
const result1 = yield Promise.resolve(1); // Pausa, devuelve promesa
console.log('Primer resultado recibido:', result1); // Reanuda con el valor
const result2 = yield new Promise(resolve => setTimeout(() => resolve(2), 500)); // Otra operación asíncrona
console.log('Segundo resultado recibido:', result2);
return 'Hecho'; // Finaliza el generador
} catch (error) {
console.error('Ocurrió un error:', error);
}
}
// "Runner" simple para el generador (simplificado)
function run(generatorFunc) {
const generator = generatorFunc();
function handleNext(newValue) {
const result = generator.next(newValue);
if (!result.done) {
if (result.value instanceof Promise) {
result.value.then(handleNext).catch(handleError);
} else {
handleNext(result.value);
}
} else {
console.log('Generador finalizado con resultado:', result.value);
}
}
function handleError(error) {
generator.throw(error);
}
handleNext(); // Iniciar el generador
}
// Ejecutamos nuestro generador asíncrono
run(myAsyncGenerator);
En este ejemplo, yield Promise.resolve(1) pausa el generador. El "runner" (o co) espera a que la promesa se resuelva y luego reanuda el generador pasando el valor 1 mediante next(1). Este valor 1 se convierte en el resultado de yield Promise.resolve(1) dentro del generador, permitiendo procesarlo en la siguiente línea (console.log). Este mecanismo permite simular un flujo de ejecución sincrónico para operaciones asíncronas.