Estoy leyendo sobre Promesas en JavaScript La guía definitiva de Flannagan 7ed. En el libro hay un script que muestra cómo construir una cadena Promise dinámicamente para un número arbitrario de URL. El guión es el siguiente:
function fetchSequentially(urls) { // We'll store the URL bodies here as we fetch them const bodies = []; // Here's a Promise-returning function that fetches one body function fetchOne(url) { return fetch(url) .then(response => response.text()) .then(body => { // We save the body to the array, and we're purposely // omitting a return value here (returning undefined) bodies.push(body); }); } // Start with a Promise that will fulfill right away (with value undefined) let p = Promise.resolve(undefined); // Now loop through the desired URLs, building a Promise chain // of arbitrary length, fetching one URL at each stage of the chain for (url of urls) { p = p.then(() => fetchOne(url)); } // When the last Promise in that chain is fulfilled, then the // bodies array is ready. So let's return a Promise for that // bodies array. Note that we don't include any error handlers: // we want to allow errors to propagate to the caller. return p.then(() => bodies); } //The script was run as below //I added the line below to declare the urls array let urls = ['/data.txt', '/readme.txt', '/textfile.txt']; //the line below is from the book fetchSequentially(urls) .then(bodies => { console.log(bodies) }) .catch(e => console.error(e)); Agregué la línea let urls para ejecutar el script para obtener 3 archivos de texto en mi PC.
Cuando se ejecuta el script, parece que solo obtiene el último archivo textfile.txt e imprime el contenido del tercer archivo 3 veces en la consola. Pensé que el script recuperaría el contenido de los 3 archivos, los agregaría a la matriz de cuerpos y luego registraría el contenido de los 3 archivos en la consola.
¿Alguien puede detectar por qué esto no funciona?
Parece que esta es la sección que está causando problemas:
for(url of urls) { p = p.then(() => fetchOne(url)); } Aquí está creando una variable global url y, dado que se ejecuta de forma asíncrona, fetchOne(url) está utilizando la última instancia de la misma.
En su lugar, puedes hacer algo como:
for(let url of urls) { p = p.then(() => fetchOne(url)); } Esto crea una instancia local de url para cada iteración.
Este tipo de estilo de programación de iteración a través de matrices de forma asincrónica puede introducir errores sutiles como este, por lo que recomendaría un estilo que sin ambigüedades cree una nueva instancia por iteración. Algo como:
urls.forEach(function (url) { p = p.then(() => fetchOne(url)); }); Aunque para este tipo de cosas con múltiples promesas, es posible que desee hacer un .map con Promise.all :
return Promise.all(urls.map(fetchOne)); // instead of promise chaining with p