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.concatrequiere 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
- ¿Es cada entrada un Iterable reutilizable o un Iterator de un solo uso que ya ha avanzado?
- ¿El consumidor necesita una interfaz de transmisión (streaming) o un arreglo en el límite?
- ¿Alguna de las entradas posee un cursor de archivo, red o base de datos que deba cerrarse ante una detención anticipada?
- ¿Los navegadores y entornos de Node de destino proporcionan
Iterator.concatnativo, 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
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 conIterator.concaty llamaría atake(100)otoArraysolo 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 mediantereturn. 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 conIterator.fromo implementarSymbol.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
toArraysolo en un límite explícito de arreglo. - Ignorar el
returnen 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.