简体   繁体   中英

How to use loops with fetch and Promise.all?

So I was following along an MDN article on promises and was wondering how to modify the following code to be able to work for any number of files (not just 3).

function fetchAndDecode(url) {
  return fetch(url).then(response => {
    if(!response.ok) {
      throw new Error(`HTTP error! status: ${response.status}`);
    } else {
      if(response.headers.get("content-type") === "image/jpeg") {
        return response.blob();
      } else if(response.headers.get("content-type") === "text/plain") {
        return response.text();
      }
    }
  })
  .catch(e => {
    console.log(`There has been a problem with your fetch operation for resource "${url}": ` + e.message);
  })
  .finally(() => {
    console.log(`fetch attempt for "${url}" finished.`);
  })
}

let coffee = fetchAndDecode('coffee.jpg');
let tea = fetchAndDecode('tea.jpg');
let description = fetchAndDecode('description.txt');

Promise.all([coffee, tea, description]).then(values => {
  console.log(values);
  // Store each value returned from the promises in separate variables; create object URLs from the blobs
  let objectURL1 = URL.createObjectURL(values[0]);
  let objectURL2 = URL.createObjectURL(values[1]);
  let descText = values[2];

  // Display the images in <img> elements
  let image1 = document.createElement('img');
  let image2 = document.createElement('img');
  image1.src = objectURL1;
  image2.src = objectURL2;
  document.body.appendChild(image1);
  document.body.appendChild(image2);

  // Display the text in a paragraph
  let para = document.createElement('p');
  para.textContent = descText;
  document.body.appendChild(para);
});

MDN specifically notes that "If you were improving this code, you might want to loop through a list of items to display, fetching and decoding each one, and then loop through the results inside Promise.all(), running a different function to display each one depending on what the type of code was. This would make it work for any number of items, not just three." I'm not sure how to do this though, and would appreciate help. Thanks.

The second part of the code could be generalised as follows:

let urls = ['coffee.jpg', 'tea.jpg', 'description.txt'];

Promise.all(urls.map(fetchAndDecode)).then(values => {
    let elem;
    for (let value of values) {
        if (value instanceof Blob) {
            elem = document.createElement('img');
            elem.src = URL.createObjectURL(value);
        } else if (typeof value === "string") {
            elem = document.createElement('p');
            elem.textContent = value;
        } else {
            console.log("unexpected value type");
            continue;
        }
        document.body.appendChild(elem);
    }
});
const resources = ['coffee.jpg', 'tea.jpg', 'description'];

const resourceRequests = resources.map(fetchAndDecode);

Promise.all(resourceRequests.then(values => {

...

Is one way to implement the suggestion. This approach allows for easier modification of the list of resources, but doesn't really change any of the Promise code.

The .map code above is equivalent to (resource => fetchAndDecode(resource)) since fetchAndDecode takes only the first argument that .map would pass to it.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM