Coding Challenge Image Loading With Promise All

The last challenge of the section, in two parts: rebuild the image slideshow with async/await, then hit the classic gotcha of .map() with an async callback returning promises instead of values, and fix it with Promise.all.

July 12, 20266 min read22 / 22Exercise

Everything from this whole section adds up here. The last post closed out the combinator functions, and this challenge is the point where async/await, .map(), and Promise.all all have to work together at once.

Part 1: Rebuild the Slideshow With async/await

Post 16 built a two-image slideshow with a flat .then() chain: load, wait, hide, load, wait, hide. First task, rewrite the consuming half with async/await instead, no .then() anywhere.

JavaScript · Live Editor
Loading editor...

Try it before reading past this point.

Building loadNPause

JavaScript · Live Editor
Loading editor...

Hit Run. Same six-step behavior as post 16, but no .then() step reads currentImg off a variable declared outside the function anymore. Reassigning the same img variable at each step works because everything now lives in one shared scope, the whole point of async/await collapsing a chain into what reads like a straight list of instructions. Comparing this against post 16's version side by side is worth doing on its own, one flat .then() chain against one straight-line function, same six steps either way.

Part 2: Loading Every Image at Once

Second task, a genuinely new problem: write loadAll(imgArr), an async function that takes an array of image paths, loads every one of them with createImage, adds a parallel class to each once they're all loaded, and does it all at the same time rather than one after another.

JavaScript · Live Editor
Loading editor...

Try it before reading past this point.

The First Attempt, and Why It Doesn't Work

The obvious move is .map() over the array, calling createImage on each path:

JavaScript
async function loadAll(imgArr) { try { const images = imgArr.map(async (path) => { const img = await createImage(path); return img; }); console.log(images); } catch (error) { console.error(`💥 ${error.message}`); } }

Log images and it's an array of three pending Promises, not three image elements, even though the images themselves really are loading in the background. The callback passed to .map() here is itself an async function, and an async function always returns a Promise, never the raw value written after return. .map() has no idea that callback is async, it just collects whatever each call returns, and what each call returns is a Promise. Running this three times in an array produces exactly three Promises, the same mismatch that first showed up trying to return a plain string from an async function a couple of posts back.

Fixing It With Promise.all

The Promises in that array are real and already resolving, so the fix isn't to change how they're created, it's to unwrap all three at once with the same combinator that ran three independent country lookups together back in post 20:

JavaScript · Live Editor
Loading editor...

Hit Run. All three images appear side by side, no waiting for one to finish before the next starts, each carrying the parallel class. imgArr.map() still returns an array of Promises exactly as before, that part never changes, Promise.all is what turns that array of Promises into an array of actual image elements, awaited together as a single unit.

Three independent requests: run one after another the total time is the sum of all three, run together the total time is only as long as the slowest one. ExpandThree independent requests: run one after another the total time is the sum of all three, run together the total time is only as long as the slowest one.

Exactly the same timing shape as post 20's country lookups, three loads overlapping instead of stacking up one after the other.

Whenever .map() runs an async callback, the result is an array of Promises, always, whether that callback loads three images, fetches three profiles, or reads three files. Promise.all on the output is the standard fix, not a special case for images specifically.

The Essentials

  1. A .then() chain and an async/await version can do the exact same six steps, the difference is readability, not capability.
  2. .map() with an async callback always returns an array of Promises, never an array of resolved values, since every async function returns a Promise regardless of what the callback returns.
  3. Promise.all is the standard fix for an array of Promises produced by .map(), unwrapping all of them into their actual values at once.
  4. This exact .map() + Promise.all combination is genuinely common, loading multiple images, fetching multiple records, anything that maps an array into async work.