Using Callback in image loading (Sequential execution) - javascript

I am playing with image loading and stuck on sequentially executing the code
function processImage(callback) {
console.log("Start");
callback('http://lorempixel.com/400/200/');
console.log("Finish");
}
function loadImage(url) {
console.log('Load Method')
var img = new Image();
img.src = url;
img.onload = function (event) {
console.log('Image Loaded')
}
}
processImage(loadImage);
For the above code, I want the output as Start -> Load Method -> Image Loaded -> Finish
Instead, I am getting Start -> Load Method -> Finish -> Image Loaded
I am having trouble using callback method I guess.
Any help appreciated.

img.onload is setting an event listener - so when the event occurs, it'll call the function. So, unless your image loads in about a millionth of second (the amount of time I guess it takes JS to go to the next line), the console.log("Finish") will happen first.
When the image is fully loaded, the function which is set to .onload is called. As the image loads after JavaScript has processed the next line, this one will happen first. If you really want to indicate the start and finish, put your finish console.log after the image loaded console.log.
img.onload = function (event) {
console.log('Image Loaded')
console.log('Finish)
}

Related

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.
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");

Why confirm() function loads before in Chrome?

I would like to know why in Chrome, this line:
var quer = confirm("Deseja esta poltrona?");
loads before the previous line:
imagens[i].src = "img/poltrona_selecionada.jpg";
On Firefox, te function works perfectly in the sequence.
This is my function:
function selecionarPoltrona() {
var imagens = document.getElementsByTagName("img");
for (var i=0; i<poltronas.length; i++) {
if (poltronas[i]) {
imagens[i].src = "img/poltrona_selecionada.jpg";
var quer = confirm("Deseja esta poltrona?");
if (quer) {
break;
} else {
imagens[i].src = "img/poltrona_disponivel.jpg";
}
}
}
}
Thank You
Welcome to the Image object, and <img> element: it is not synchronous. When you tell an image what its source is, the rest of your code gets to keep running. It does not wait for the image binding to succeed, the image URL to be looked up, the data the be transfered, the bytes to be decoded, all before you see the image. This all happens while the rest of your code runs. However, confirm (and alert, and prompt, and all those terrible functions) don't: they block the thread. So this happens:
You set the image .src attribute, and the browser schedules this for off-thread handling. Your code continues
confirm gets triggered, and everything stops. Nothing will happen in your tab until the confirm is deal with.
you click through the confirm. Javascript runs again, and the browser's image source binding runs again.
after X amount of time, your image is done downloading, parsing, and finally renders.
If you want some code to run after the image finishes all its work, you do this:
...
img.onload = function() {
// the code you need to have run after the image finishes loading.
}
img.onerror = function() {
// whatever we need to do if the image cannot load.
}
img.src = "..." // trigger the image loading attempt
Or, using modern rather than 1998 JS:
...
img.addEventListener("load", function(evt) {
// the code you need to have run after the image finishes loading.
});
img.addEventListeners("error", function(evt) {
// whatever we need to do if the image cannot load.
});
img.src = "..." // trigger the image loading attempt

data:image/jpeg;base64 how to get its width and height in pixels

How do you get the width and height in pixels image which its src is in data:image/jpeg;base64 ?
Didn't succeed using setting img.src to it and then calling width().
var img = jQuery("<img src="+oFREvent.target.result+">");
img.width() // = 0
This will work :
var img = new Image();
img.onload = function() {
alert(this.width);
}
img.src = "data:image/jpeg;base64,/9j/4AAQSkZJRgABAgAA.......";
FIDDLE
I made sure the base64 was valid by converting an image here, and the onload function should come before setting the source of the image.
You have to wait for the image to be loaded (or complete, if cached). This will result in an asynchronous callback operation:
// pure JS:
var img = new Image();
img.src = myDataString;
if (img.complete) { // was cached
alert('img-width: '+img.width);
}
else { // wait for decoding
img.onload = function() {
alert('img-width: '+img.width);
}
}
Note: I once had the same problem with a project using jQuery. jQuery doesn't provide an access to the image directly enough just after you have created it. It seems, it's not possible to be done, but in pure JS. But you could try a timeout-loop and wait for the img-width to have a value (and catch any loading/decoding errors).
[Edit, see also comments] Why this works (why there is no race-condition):
JS is single-threaded, meaning there's only one part of code executed at a given time. Any events will be queued until the current scope is exited and will only fire then. Thanks to late-binding, any listeners will be evaluated only then (after the current scope has been executed). So the handler will be present and listening as soon as the onload-Event is fired, regardless, if the listener was setup before or after setting the src-attribute. In contrast to this, the complete-flag is set as soon as the src-attribute is set.

Is it necessary to set onload function before setting src for an image object?

I was told it is necessary to set the onload function before setting src for an image object. I've searched in SO for this.
I found this code:
var img = new Image();
img.src = 'image.jpg';
img.onload = function () {
document.body.appendChild(img);
};
But most people believe that onload should be written before src like this:
var img = new Image();
img.onload = function () {
document.body.appendChild(img);
};
img.src = 'image.jpg';
MUST it be written in this order? Are there any cases when the above code will cause an error (like if an image is too big)?
If you anyone can show me some examples, I will be very appreciate.
It doesn't have to, but if setting the src and the image loads before your handler is attached, it won't fire.
JavaScript operates asynchronously. Setting the src will cause the web browser to load the image outside the main execution flow. If onload isn't set at the time that operation completes - which could be between setting src and onload.
As soon as you assign the src a value, the image will load. If it loads before the onload is reached, your onload will not fire.
To support ALL implementations, I strongly suggest to assign the onload handler before setting the src.
It is my experience (21+ years of JS) that you MUST set onload first - especially in IE which did not even support the image object when I started with JS.
If you get issues about the cached image not firing, add +"?"+new Date().getTime() when you set the src next time to avoid cache.
Here is the example from MDN which also uses the order I have suggested
Creating an image from scratch
Another SO link image.onload not firing twice in IE7
The browser will start downloading the image asychronously as soon as you assign a src, so there is the possibility the download could complete before you attach the onload event handler and never fire the code to add the image to the DOM.
Most browser fire the load event imediatly if the image if the image is cached.
However, Internet explorer 7 won't fire it at all. That's why it's better to set the src first.
Answers above always mentioned the same problem like:
there is the possibility the download could complete before you attach the onload event handler
or
but if setting the src and the image loads before your handler is attached, it won't fire.
or bug with IE7.
First, let's ignore IE7.
Second, I don't think problem mentioned exist, for example:
function loadImg(url) {
let img = new Image()
img.src = url
let date1 = Date.now()
console.log(img.complete)
while (Date.now() - date1 < 5000) {
}
img.onload = function() {
console.log('success')
}
console.log('sync first')
}
loadImg('https://cdn.sstatic.net/Sites/stackoverflow/img/sprites.svg')
Normally, you will get:
false
sync first
success
Well, if you executed on another site which will use cache more than one time. You will get the result like image below:
The answer is clear now.
Well, as long as you set the onload synchronously. You will not miss the onload event.
Why would I say synchronously. For another example,
function loadImg(url) {
let img = new Image()
img.src = url
let date1 = Date.now()
console.log(img.complete)
while (Date.now() - date1 < 5000) {
}
setTimeout(function() {
img.onload = function() {
console.log('success')
}
})
console.log('sync first')
}
loadImg('https://cdn.sstatic.net/Sites/stackoverflow/img/sprites.svg')
The result on another site:
The second time with cache, load event won't be triggered. And the reason is setTimeout is asynchronously.

How do i make a function so that the programming line will wait until it's done and move to the next line in javascript

I'm creating an HTML5 with Javascript,
but sometimes the code is executing the next line of statement
before the current line is finished,
For example,
I'm passing an image to an object in Javascript,
but then the next statement is executed already before the object has the image,
so sometimes the object has no image, but sometimes it has,
So between the 2 lines I need to assign something like callback or wait function,
but i dont know how
var image=new Image();
ws.send("complete");
Javascript is (mostly) asynchronous, which is a good thing. You dont want to block the UI while waiting on an image for example.
Use the following to execute ws.send after the images has completed:
var image = new Image();
image.onload = function()
{
ws.send("complete");
}
image.src = 'image.jpg';
If you would do this synchronous the whole program would block, and you dont want that.
As for callbacks, you can just pass a function as an argument to your function. For example:
function doSomething(callback)
{
callback();
}
doSomething(function()
{
console.log('im done!');
});
Not sure if your question is image specific, but for images, you can do the following:
The onload event will fire once the image is loaded.
var im = new Image();
im.onload = function() { ws.send('complete'); }
im.src = 'path/to/image.jpg'
take a look at this: Javascript image onload
essentially you have to write an eventlistener that catches the event when the image is loaded.

Categories