Prefetching  Images in JS
Tags: javascript, tutorial, programming, webdev
Published: 2021-01-19 13:05:02

_Loading..._

These are mad times we live in, mad! There is an insatiable demand for a silky smooth user experience, instantaneous loading times, and unlimited content.

When thinking about load times, one thing that comes to mind is images. Have you ever experienced the frustration of being on a loaded webpage, only to have to wait for images to load on further interaction with the page. _Of course you have._ If you are developing a website with many images you will undoubtedly want to spare your users from this vexation.

Luckily, there is a simple way to go about this if:  
1. You have the image URLs ahead of time  
2. You can accurately predict which images the user will likely see next  

If the above isn't true then this article may not be super useful to you.

### Basic Prefetching
```js
const urls = [
  "shorturl.at/auLWY",
  "shorturl.at/sBTX5"
];
urls.forEach(imageUrl => {
    const imageElement = new Image(); // Create new image element
    imageElement.onload = () => {}; // Do something on success
    imageElement.onerror = () => {}; // Do something on error
    imageElement.src = imageUrl; // Set the image URL which will trigger a network request
};
```

Let's talk about why the approach above actually works.

The `Image` constructor, which is functionally equivalent to `document.createElement('img')`, creates a new image instance. The purpose of creating these images is not for display, but rather to set the image `src` attribute which in turn will trigger a network request for the image. If you can accurately predict which images the user is likely to see next based on some logical flow of interaction, then you can ensure the images have already been delivered to the browser by the time the user gets there. 

This may seem trivial, but for large scale applications it can save a significant amount of time. In order for this to work effectively, you will need to find a practical place to prefetch in your current environment: **before** the user sees the images and **after** any heavy initialization tasks as to not affect your web application's initial load time.

### Prefetching w/Promises
```js
const urls = [
  "shorturl.at/auLWY",
  "shorturl.at/sBTX5"
];
Promise.all(urls.map(imageUrl => {
  return new Promise((resolve, reject) => {
    const imageElement = new Image(); // Create new image element
    imageElement.onload = () => { resolve(); }; // Do something on success
    imageElement.onerror = () => { reject(); }; // Do something on error
    imageElement.src = imageUrl; // Set the image URL which will trigger a network request
  };
}))
.then(() => {}) // Do something after all images load
.catch((err)=> {}); // Do something if there are any errors
```
Additionally, if find it necessary to have more control over the loading state of your images, such as triggering some function when they **all** load successfully or to conditionally display some skeleton component, there is an easy way to do so using promises.

Simply map each image url to a new promise and resolve/reject the promise as it suits your needs. Wrap the promise array in a `Promise.all` and use the `then` and `catch` methods to execute the appropriate action. That's it!

I hope this article has helped you understand how you can use prefetching to improve your web application's user experience. Let me know in the comments if it works for you!