Tema representativo de entrevista

Entrevista general: ¿Cómo compone Iterator.concat fuentes de datos perezosas (lazy)?

GeneralDifícil
Equipo editorial de Offer.ccPublicado Actualizado

Pregunta

Implemente un iterador perezoso (lazy) que combine un arreglo, un Set y un generador, y luego explique los límites entre Iterator.concat, Array.concat, Iterator.from y flatMap.

1. Planteamiento y alcance

Un agregador de registros lee un arreglo en memoria, un Set deduplicado y un generador paginado en orden de prioridad; el consumidor solo necesita los primeros 100 registros. Utilice Iterator.concat preservando la evaluación perezosa (laziness) y explique las entradas infinitas, las excepciones y la limpieza cuando el consumo se detiene anticipadamente.

2. Qué está evaluando el entrevistador

  • Distinguir Iterable de Iterator y saber que Iterator.concat requiere argumentos Iterable.
  • Explicar que el resultado es un nuevo Iterator perezoso que extrae las entradas en secuencia solo cuando se consumen.
  • Identificar que una fuente infinita oculta todas las fuentes posteriores y que la lista de argumentos debe permanecer finita.
  • Manejar la limpieza de return, casos de TypeError, alternativas de compatibilidad (fallback) y la materialización innecesaria de arreglos.

3. Preguntas para aclarar primero

  1. ¿Es cada entrada un Iterable reutilizable o un Iterator de un solo uso que ya ha avanzado?
  2. ¿El consumidor necesita una interfaz de transmisión (streaming) o un arreglo en el límite?
  3. ¿Alguna de las entradas posee un cursor de archivo, red o base de datos que deba cerrarse ante una detención anticipada?
  4. ¿Los navegadores y entornos de Node de destino proporcionan Iterator.concat nativo, o se permite un polyfill?

4. Respuesta de treinta segundos

Verificaría que cada argumento implemente Symbol.iterator y luego crearía un Iterator perezoso con Iterator.concat. Este adquiere cada entrada en orden y lee valores solo cuando el consumidor llama a next, take o propaga (spreads) el resultado. take(100) detiene la extracción y le da al iterador subyacente la oportunidad de cerrarse. Coloque las entradas infinitas al final o limite el consumo; en entornos de ejecución más antiguos, utilice una alternativa basada en generadores con la misma semántica de pereza y limpieza.

5. Análisis detallado paso a paso

Paso 1: Establecer el límite del Iterable

Iterator.concat acepta Iterables, no simplemente objetos con next. Los arreglos, los Sets y los objetos generadores califican. Un Iterator simple primero debe envolverse con Iterator.from; esto le da a concat una forma clara de obtener y cerrar cada iterador de entrada.

Paso 2: Construir la composición perezosa

js
function* pages() {
  yield { source: "page", id: 1 };
  yield { source: "page", id: 2 };
}

const memory = [{ source: "memory", id: 1 }];
const unique = new Set([{ source: "set", id: 1 }, { source: "set", id: 2 }]);
const merged = Iterator.concat(memory, unique, pages());
const firstThree = merged.take(3).toArray();

Crear merged no recorre ninguna entrada; toArray inicia el consumo. Los valores se emiten en el orden de memoria, Set y páginas, y concat no los deduplica ni los transforma.

Paso 3: Entender el estado de consumo

El Iterator retornado almacena la entrada y la posición actuales. Después de que un consumidor lo avanza, otro consumidor ve únicamente el resto. Para reproducirlo se requieren nuevos Iterators a partir de los Iterables de origen. Si solo se necesitan los primeros N valores, aplique take después de concat en lugar de propagar primero.

Paso 4: Limitar las entradas infinitas

Cualquier entrada puede ser infinita, lo que hace que el resultado sea infinito. Si la primera entrada nunca termina, las entradas posteriores son inalcanzables; coloque los lotes finitos primero o aplique take, tiempos de espera (timeout) y cancelación en el límite del consumidor. No escriba Iterator.concat(...infiniteIterables()), porque propagar la lista de argumentos nunca termina.

Paso 5: Limpieza, errores y alternativa de compatibilidad

Cuando el consumo se detiene o lanza un error, el iterador actual debe tener la oportunidad de ejecutar return, liberando cursores, identificadores de archivos o conexiones. Un iterador personalizado puede registrar la limpieza en finally. En entornos de ejecución sin soporte nativo, un generador puede aplicar yield* a las entradas en orden y propagar return; la alternativa no debe copiar silenciosamente cada entrada en un arreglo.

6. Respuesta modelo de alta calidad

Validaría las entradas Iterable, crearía una composición perezosa con Iterator.concat y llamaría a take(100) o toArray solo en el límite del consumidor. concat adquiere las entradas en orden y no deduplica sus valores. Una primera entrada infinita hace que las fuentes posteriores sean inalcanzables, por lo que limitaría el consumo y probaría la limpieza ante detenciones anticipadas mediante return. Para entornos de ejecución más antiguos, una alternativa con generadores puede preservar la pereza, el orden, el estado de un solo uso y la limpieza sin materializar las fuentes.

7. Errores comunes

  • Pasar un objeto solo con next → TypeError → envolverlo con Iterator.from o implementar Symbol.iterator.
  • Asumir que concat se ejecuta de inmediato → E/S oculta → recordar que el consumo extrae valores bajo demanda.
  • Poner un Iterable infinito primero → las fuentes posteriores nunca se alcanzan → reordenar y limitar el consumo.
  • Propagar todo primero → picos de memoria y latencia → llamar a toArray solo en un límite explícito de arreglo.
  • Ignorar el return en detenciones anticipadas → fugas de cursores → probar un Iterable de conteo y asegurar la limpieza.

8. Preguntas de seguimiento

Pregunta de seguimiento 1: ¿En qué se diferencia de Array.concat?

concat de arreglos procesa los arreglos materializados de inmediato y devuelve un arreglo. Iterator.concat acepta Iterables y devuelve un Iterator perezoso que puede representar generadores y fuentes infinitas.

Pregunta de seguimiento 2: ¿Por qué no pasar cada Iterator directamente?

Un Iterator simple que no es Iterable deja en la ambigüedad la propiedad y el cierre de las entradas que aún no se han alcanzado. Envuélvalo con Iterator.from para establecer el límite de Iterable.

Pregunta de seguimiento 3: ¿Cuándo es preferible flatMap?

Cuando la entrada en sí es una secuencia grande o ilimitada de Iterables, flatMap puede producirlos y aplanarlos incrementalmente. concat es más claro para un conjunto finito y conocido de entradas.

Pregunta de seguimiento 4: ¿Cómo verifica la limpieza?

Utilice un Iterable de prueba que implemente next y return, registre las extracciones y los cierres, y compruebe que take, la interrupción del consumidor y los errores en las devoluciones de llamada (callbacks) detengan la extracción y llamen a return.

Fuentes públicas

Preguntas relacionadas