cover

Entendiendo yield*: El Operador de Delegación en JavaScript y Effect


Escuchar este post

Selecciona una voz y genera audio para escuchar este post

Una guía profesional para principiantes que quieren dominar la composición de generadores y efectos

Introducción

Si alguna vez te has encontrado con yield* en código JavaScript o TypeScript y te has preguntado "¿qué demonios hace este asterisco?", estás en el lugar correcto. Este pequeño operador es una de las herramientas más elegantes pero menos comprendidas del lenguaje.

Parte 1: yield* en JavaScript Nativo

¿Qué es un Generador?

Antes de entender yield*, necesitamos claridad sobre los generadores. Un generador es una función especial que puede pausar su ejecución y reanudarla más tarde:

function* simpleGenerator() { console.log('Inicio'); yield 1; // Pausa aquí console.log('Medio'); yield 2; // Pausa de nuevo console.log('Fin'); return 3; } const gen = simpleGenerator(); console.log(gen.next()); // { value: 1, done: false } console.log(gen.next()); // { value: 2, done: false } console.log(gen.next()); // { value: 3, done: true }

La Magia de yield*

Ahora, imagina que tienes varios generadores y quieres combinarlos. Aquí es donde yield* brilla:

function* generadorNumeros() { yield 1; yield 2; } function* generadorLetras() { yield 'A'; yield 'B'; } function* generadorCombinado() { yield* generadorNumeros(); // Delega a generadorNumeros yield* generadorLetras(); // Delega a generadorLetras yield 'Fin'; } // Uso: const resultado = [...generadorCombinado()]; console.log(resultado); // [1, 2, "A", "B", "Fin"]

Caso Práctico: Recorriendo un Árbol

Uno de los usos más elegantes de yield* es recorrer estructuras anidadas:

class NodoArbol { constructor(valor, hijos = []) { this.valor = valor; this.hijos = hijos; } // Generador para recorrer el árbol en profundidad *recorrer() { yield this.valor; for (const hijo of this.hijos) { yield* hijo.recorrer(); // Delega recursivamente } } } const arbol = new NodoArbol(1, [ new NodoArbol(2, [new NodoArbol(4)]), new NodoArbol(3) ]); console.log([...arbol.recorrer()]); // [1, 2, 4, 3]

Parte 2: yield* en Effect (TypeScript)

El Mundo de Effect

Effect es una biblioteca que trae programación funcional a TypeScript. Usa Effect.gen con generadores para manejar operaciones asíncronas de manera elegante.

La Diferencia Crucial

En Effect, yield* no delega a otro generador, sino que aplana (flatten) un Effect que devuelve otro Effect:

import { Effect } from "effect" // ❌ Sin yield* (anidado y confuso) const operacionAnidada = Effect.gen(function* () { const innerEffect = Effect.succeed(42) const resultado = yield* innerEffect // resultado: number return resultado }) // ✅ Con yield* (limpio y directo) const operacionDirecta = Effect.gen(function* () { return yield* Effect.succeed(42) // resultado: number })

Ejemplo Práctico con Effect

Veamos un ejemplo más realista de cómo usar yield* en una aplicación:

import { Effect } from "effect" // Simulamos operaciones asíncronas const obtenerUsuario = (id: string) => Effect.promise(() => fetch(`/api/usuarios/${id}`).then(res => res.json())) const validarPermisos = (usuario: any) => Effect.succeed(usuario.rol === 'admin') const obtenerDatosProtegidos = () => Effect.promise(() => fetch('/api/datos-protegidos').then(res => res.json())) // Flujo completo usando yield* const flujoAplicacion = Effect.gen(function* () { // Cada yield* espera el resultado y lo extrae automáticamente const usuario = yield* obtenerUsuario("123") const tienePermisos = yield* validarPermisos(usuario) if (!tienePermisos) { return { error: "Sin permisos" } } const datos = yield* obtenerDatosProtegidos() return { usuario, datos } }) // Ejecutar el efecto Effect.runPromise(flujoAplicacion) .then(resultado => console.log(resultado))

Beneficios Clave de yield* en Effect

  1. Código más limpio: Evita el anidamiento complejo
  2. Tipado seguro: TypeScript infiere correctamente los tipos
  3. Manejo automático de errores: Los errores se propagan naturalmente
  4. Composición natural: Fácil de combinar múltiples operaciones

Comparación Visual

// ❌ Sin yield* - código anidado const ejemploAnidado = operacion1().pipe( Effect.flatMap(valor1 => operacion2(valor1)) ); // ✅ Con yield* - código secuencial const ejemploSecuencial = Effect.gen(function* () { const valor1 = yield* operacion1(); return yield* operacion2(valor1); });

Comparativa rápida

En JavaScript Nativo:

  • Propósito: Delegar el control a otro generador.
  • Resultado: Se consumen todos los valores del generador delegado antes de continuar.

En Effect (TypeScript):

  • Propósito: Extraer el valor de un Effect anidado.
  • Resultado: Se obtiene el valor interno directamente, manteniendo el código plano y legible.

Mejores Prácticas

En JavaScript:

  • Usa yield* para componer generadores complejos
  • Ideal para recorridos recursivos de estructuras
  • Mantiene el código limpio y modular

En Effect:

  • Siempre usa yield* con Effects dentro de Effect.gen
  • Evita anidamiento innecesario
  • Mantiene la cadena de tipos intacta
  • Propaga errores automáticamente
  • Facilita la composición de operaciones

El operador yield* es una herramienta poderosa que, dependiendo del contexto, puede delegar generación o aplanar estructuras. Dominarlo te permitirá escribir código más elegante, modular y mantenible.

Ya sea que estés construyendo generadores complejos en JavaScript puro o manejando efectos sofisticados en TypeScript, yield* es tu aliado para la composición limpia de código.


¿Tienes preguntas sobre yield* o quieres ver ejemplos más avanzados? ¡Únete a nuestra lista de correo!

Abrazo. Bliss. 🤓

meta cover

20 años de Git

Checa este otro Post

meta cover

AbortController & AbortSignal

Checa este otro Post

¡Nuevo curso!

Animaciones web con React + Motion 🧙🏻