0

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.

5
  • There's nothing specific to promises about this. The promise code itself will not change. It's just replacing the repetitive parts of the code with loops that do the same thing. Commented Jan 11, 2021 at 20:17
  • @Bergi But I mean syntax-wise, how can I do something like files.length? Would I need to first fetch everything and then create an array so I know how many files there are? Then, how would I know what argument to pass it for its name? Commented Jan 11, 2021 at 20:20
  • Start with const fileNames = ['coffee.jpg', 'tea.jpg', 'description.txt']. Commented Jan 11, 2021 at 20:25
  • values is already an array, so think about looping through that using foreach. but you also need to think about file types. that is, you need to find a way to pass in the type of the file along with the file content, since you need to create the appropriate html element based on the type. right now it knows the type for each because it's hard coded. Commented Jan 11, 2021 at 20:28
  • @muratgu You can already distinguish the values, images become Blob instances while text files become strings. Commented Jan 11, 2021 at 20:49

2 Answers 2

2

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); } }); 
Sign up to request clarification or add additional context in comments.

Comments

0
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.

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.