¿Observable en un bucle for?
Me gustaría llamar a un servicio REST observable en un bucle for. Retorna un archivo CSV codificado en base64. Me gustaría decodificarlo y concatenarlo en una sola cadena y retornarlo. Después de eso, intento suscribirme a ese método y hacer clic en el DOM para descargarlo. Obtengo una cadena vacía con solo "\r\n"
. ¿Por qué no espera a que el REST retorne el archivo antes de devolverlo?
downloadFilesAndConcatenate(): Observable<any> {
let concatenatedFileDecoded: string = '\r\n';
for (let i = 0; i < this.fileIDs.length; i++) {
this.restService.getFile(this.fileIDs[i]).subscribe(response => {
this.fileResultSet = response;
this.message = response.message;
this.file = this.fileResultSet.result;
let fileCSVbase64 = this.file.fileBytes
let fileCSVDecoded = atob(fileCSVbase64);
concatenatedFileDecoded += fileCSVDecoded;
},
error => {
this.message = error.error.message;
});
return new Observable( observer => {
observer.next(concatenatedFileDecoded)
observer.complete();
});
}
}
Y luego me suscribo a él:
descargar() {
if (this.dateEnd !== null && typeof this.dateEnd !== "undefined") {
debugger;
this.downloadFilesAndConcatenate() // Múltiples archivos
.subscribe(
(result) => {
debugger;
const link = document.createElement( 'a' );
link.style.display = 'none';
document.body.appendChild( link );
const blob = new Blob([result], {type: 'text/csv'});
const objectURL = URL.createObjectURL(blob);
link.href = objectURL;
link.href = URL.createObjectURL(blob);
link.download = this.file.name;
link.click();
},
(err) => {
console.error(err);
},
() => console.log("descarga observable completa")
);
} else {
this.downloadFile(); // Solo un archivo
}
}
davy.ai
El problema es que la función
downloadFilesAndConcatenate()
no espera a que todas las llamadas al servicio REST se completen antes de devolver el archivo concatenado. La instrucciónreturn new Observable
está dentro del buclefor
y se llama después de la primera llamada al servicio REST, por lo que solo devuelve un observable con el archivo concatenado de la primera llamada.Para solucionar esto, puedes utilizar el operador
forkJoin
de la libreríarxjs
para combinar todas las llamadas al servicio REST y esperar a que se completen antes de devolver el archivo concatenado. Así es como puedes modificar la funcióndownloadFilesAndConcatenate()
:Este código crea un array de observables que llaman al servicio REST para cada archivo, decodifica el archivo base64 en una cadena y luego utiliza el operador
forkJoin
para combinar todos los observables en un único observable que emite un array de cadenas de archivo. Finalmente, se utiliza el operadormap
para concatenar todas las cadenas de archivo en una única cadena separada por'\r\n'
.La función
download()
debería funcionar como antes, pero ahora recibirá la cadena de archivo concatenada desde el observable devuelto pordownloadFilesAndConcatenate()
.