Skip to content
Advertisement

Can image load event be synchronous, or will it always be asynchronous?

Let’s say I have the following code:

let img = document.createElement("img");
img.addEventListener("load", function() {
    alert("Loaded!");
});
img.src = external_string; // Can "load" fire here?

The question is – if the image in external_string is already in browser cache, or maybe is base64 encoded, or whatever – can it so happen that the load event is called immediately at the same line as src is being assigned?

Note: I actually want it to be asynchronous because I want to set up a few more things before the load event gets fired. But if there is a chance that it might get fired synchronously then I need to put in extra work to postpone that.

I could not find this subtlety explained in any document, but practical tests always call the event asynchronously.

Advertisement

Answer

You can use the decode() method to wait for images to be ready. Even if the image loads immediately, queuing the promise micro-task will always ensure code runs after the promise resolves

Promise handlers .then/.catch/.finally are always asynchronous.

Even when a Promise is immediately resolved, the code on the lines below .then/.catch/.finally will still execute before these handlers.

// Base64 data URI requires no loading time
const red_dot = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAUAAAAFCAYAAACNbyblAAAAHElEQVQI12P4//8/w38GIAXDIBKE0DHxgljNBAAO9TXL0Y4OHwAAAABJRU5ErkJggg==";

const img = document.createElement("img");
img.src = red_dot;
img.decode().then(() => {
  console.log("img ready, this will log second");
  document.body.append(img);
});
console.log("img loading, this will log first");

Keep in mind that you cannot call decode() before setting src.


The load event will also always trigger and a listener can be added at any time however since it relies on the event loop, control over the order of operations is non-deterministic. That being said, any procedural code you add after setting the src will always execute before the event handler fires.

// Base64 data URI requires no loading time
const red_dot = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAUAAAAFCAYAAACNbyblAAAAHElEQVQI12P4//8/w38GIAXDIBKE0DHxgljNBAAO9TXL0Y4OHwAAAABJRU5ErkJggg==";

const img = document.createElement("img");
img.addEventListener("load", () => {
  console.log("img ready, this will log second");
  document.body.append(img);
});
img.src = red_dot;
console.log("img loading, this will log first");
User contributions licensed under: CC BY-SA
1 People found this is helpful
Advertisement