How to execute the following ES6 lines in sequence? - javascript

I'm using the following function to load high-res version of images once they have loaded:
store.loadFullPano = (pano) => {
$('#panos-wrapper').addClass('blurred')
setTimeout(function () {
const image = new Image()
image.src = pano.url
image.onload = () => {
pano.url = pano.urlHigh
$('#panos-wrapper').removeClass('blurred')
}
}, 1000)
}
As you can see I'm blurring the low-res images with CSS. It works...except the low-res image shows for a few seconds after the hig-res one has been replaced and the blur has been removed.
I think that's because the two lines inside image.onload are being executed at the same time (or the last one first).
Is there a way to execute the first one and only then the last one?
EDIT:
I used a promise:
var p = new Promise(function (resolve, reject) {
pano.url = pano.urlHigh
console.log('1')
resolve(pano.url)
})
p.then(function () {
$('#panos-wrapper').removeClass('blurred')
console.log('2')
})
But the low-res is still showing for a few seconds. What's a possible solution for this?

From the Vue docs:
you can use Vue.nextTick(callback) immediately after the data is changed. The callback will be called after the DOM has been updated
and
There is also the vm.$nextTick() instance method, which is especially handy inside components, because it doesn’t need global Vue and its callback’s this context will be automatically bound to the current Vue instance
Depending on how it is implemented, however, I'm not sure that "the DOM has been updated" will be enough though, since loading an image may need more time. Maybe by then though, if you can obtain the updated/created hi-res image object though, maybe you can wait for its onload...
Update: It looks like you will need to utilize the API to obtain the image element and to use its onload listener since images do not load synchronously. Apparently you can get this with the $el property.
So I'd try updating to something like this:
image.onload = () => {
pano.url = pano.urlHigh
pano.$el.onload = function () {
$('#panos-wrapper').removeClass('blurred')
};
}
v-el might also be handy.
Update 2:
If $el doesn't find your image element, I'd try this:
<img v-bind:src="pano.url" v-el:image>
and then:
image.onload = () => {
pano.url = pano.urlHigh
pano.$els.image.onload = function () {
$('#panos-wrapper').removeClass('blurred')
};
}

The browser will be executing these lines in sequence. Add a breakpoint above the first line then just use the step-by-step debugger to follow the flow.
image.onload = () => {
debugger;
pano.url = pano.urlHigh
$('#panos-wrapper').removeClass('blurred')
}
I suspect that they seem out of order because pano.url = pano.urlHigh probably doesn't have an immediate effect.
It's a synchronous statement and JavaScript is single threaded, which means that even if you've got some other code that responds to changing the url of pano, it can't execute until it's finished executing all the synchronous code.
pano.url = pano.urlHigh
// there's no time for any other code to happen elsewhere here.
$('#panos-wrapper').removeClass('blurred')
The jQuery call is guaranteed to be the next thing that happens after you set the url property.
If you're using a template engine that reacts to changes in the properties of pano, then the best way to run the jQuery call after the changes have been made, is to delay it til the next frame.
pano.url = pano.urlHigh
requestAnimationFrame(function() {
$('#panos-wrapper').removeClass('blurred')
});

Related

javascript : calculating and returning values after Image loaded

I already checked multiple responses to asynchronous javascript behaviours and experimented with callbacks, experimenting with promises next, but parallell I want to send SoS request here :/
Because often first answers are ("What do you want to do?")
I have to find out all the non-empty or transparent pixels in an Image.(kinda like a mask) and give them back an array of binary arithmetical numbers.
What my program does in short:
it gets Image URL and creates new Image() from it.
it creates a Canvas and adds it to DOM
in image.onLoad() it draws the image on the canvas
after the Image is on canvas, it scans every pixel for its color and gives back a data array.
My problem is to force the calculation to WAIT until the image is loaded. I tried to do with something like this:
return getDataArray(image.onLoad()= function(){
// ...init things...
}));
Still, it goes into the getDataArray function, before the image.onLoad happens.
I'm gonna take a break and walk outside because I'm out of productive ideas.
Here is the original function:
getImageScan: function() {
this.myImage.src = imageScanner.imgUrl;
var is = this;
return is.getArrayFromCanvas(this.myImage.onload = function () {
is.imageHeight = is.myImage.height;
is.imageWidth = is.myImage.width;
is.appendCanvasToBody();
is.initGraphicalContent();
is.drawImageOnCanvas();
console.log("image is loaded");
})
},
getArrayFromCanvas: function () {
console.log("request for calculations");
var booleanJson = this.getJsonFromCanvas()
return this.getBinaryArithmeticFromBooleans(booleanJson);
}
and this is the result
request for calculations
[]
the image is loaded
here is the entire *.js if you want more information (it's my private project in slacktime, so no copyright issues):
https://github.com/Vilkaz/gridToImage/blob/master/web/resources/js/imageScanner.js
You try to pass something to getArrayFromCanvas although it has no parameters. I don't understand why you do this, but I guess you want something like this:
getImageScan: function(callback) {
this.myImage.src = imageScanner.imgUrl;
var is = this;
this.myImage.onload = function () {
is.imageHeight = is.myImage.height;
is.imageWidth = is.myImage.width;
is.appendCanvasToBody();
is.initGraphicalContent();
is.drawImageOnCanvas();
console.log("image is loaded");
callback(is.getArrayFromCanvas());
}
}
One difference between the asynchronous behavior above and your original code is that getImageScan returns immediately and that a callback is called later to "return" the result.

Wait for image loading to complete in JavaScript

I'm loading images with JavaScript. Something like this:
images[0]=new Image();
images[0].onload=function(){loaded++;console.log(loaded)};
images[0].src="assets/img/image.png";
When I look at the log, I see that all the images are loaded nicely, since the value of the "loaded" variable increases with each loaded image.
However I would like to stop any further action to be executed until this amount reaches it's maximum, so right after setting up the images, I place a while cycle.
while(loaded<11){
document.getElementById("test").innerHTML="Loading "+loaded+"/11";
console.log(loaded);
}
//Some code here which should only run after everything has been loaded
//In other words: when the statement in the while cycle becomes false
However my browser simply crashes, since the while seems to be stuck in an infinite loop. When I check the log, I see that "0" was written 1000 times, and after that, the numbers from 1 to 11 (which implies that the images in fact gets loaded, but the while does not care about it, and crashes faster than it could happen).
I believe that the method I'm trying to use here is not the right approach to solve this problem.
How can I put everything on hold until every asset which is needed for the site is loaded?
Using promises and async functions, there is a nice way to wait until all the images are loaded (no callbacks, no loaded image counting):
async function loadImages(imageUrlArray) {
const promiseArray = []; // create an array for promises
const imageArray = []; // array for the images
for (let imageUrl of imageUrlArray) {
promiseArray.push(new Promise(resolve => {
const img = new Image();
// if you don't need to do anything when the image loads,
// then you can just write img.onload = resolve;
img.onload = function() {
// do stuff with the image if necessary
// resolve the promise, indicating that the image has been loaded
resolve();
};
img.src = imageUrl;
imageArray.push(img);
}));
}
await Promise.all(promiseArray); // wait for all the images to be loaded
console.log("all images loaded");
return imageArray;
}
Or you can wait for a single image to load:
async function loadImage(imageUrl) {
let img;
const imageLoadPromise = new Promise(resolve => {
img = new Image();
img.onload = resolve;
img.src = imageUrl;
});
await imageLoadPromise;
console.log("image loaded");
return img;
}
You can use it like this (using promise chaining):
loadImages(myImages).then(images => {
// the loaded images are in the images array
})
Or inside an async function:
const images = await loadImages(myImages);
Personally I hate using while()... I think the easiest way to do it, is using event listeners.
var img = new Image;
img.addEventListener("load", function () {
//Img loaded
});
img.src= e.target.result;
Javascript is single threaded. This means that if you add an event listener that listener will not run until the current execution has completed. Hence if you start a loop that relies on a event to end it it will never happen as the event will never fire because the current execution is preventing it from running. Also the events are place on the call stack asynchronously, thus if your execution is slower than the rate of event firing (placing a call on the call stack) you also risk the a page crash. This is a common mistake when using setInterval when the interval is set to less time than the code takes to execute. NEVER USE setInterval.
Just remember Javascript can not do two things at once.
The best way to deal with resource monitored loading is to use setTimeout.
var allLoaded = false;
var imgCount = 0; // this counts the loaded images
// list of images to load
const imageURLS =["a.jpg","b.jpg","c.jpg","d.jpg","e.jpg"];
// array of images
var images = [];
const onImageLoad = function(){ imgCount += 1; } // onload event
// loads an image an puts it on the image array
const loadImage = function(url){
images.push(new Image());
images[images.length-1].src = url
images[images.length-1].onload = onImageLoad;
}
const waitForLoaded = function(){
if(imgCount === images.length){
allLoaded = true; // flag that the image have loaded
}else{
// display the progress here
...
setTimeout(waitForLoaded,100); // try again in 100ms
}
}
// create the images and set the URLS
imageURLS.forEach(loadImage);
setTimeout(waitForLoaded,100); // monitor the image loading

jQuery: Detecting if browser is done rendering after append()

In a jQuery project, I am adding several images to a page after it has finished loading, using the append() function. After all the appends are done, I need to call a function that detects the width and height of each of these new elements, so I can properly align them in the page.
This is working well on my rather slow development PC. But when testing it on a fast machine in a quick browser (e.g. Chrome), it turns out that calling the layout function right after the appends are done leads to random, weird behavior. Executing the layout function again with a slight delay solves the problem. I guess "javascript is done appending the new elements" is not the same as "the browser is done displaying them", so measuring their size is not yet possible.
So what I need is some way to tell when the new elements are actually there, so that I can reliably detect their widths and heights. I don't think document.ready() would help in this case, since the elements are added by a function that is called in document.load().
This is the code I copied from another stackoverflow thread (by a dude named Pointy, thanks by the way!)
function called_by_document_load() {
var promises = [];
for (var i=0; i<my_pics.length; i++) {
(function(url, promise) {
var img = new Image();
img.onload = function () {
promise.resolve();
};
img.src = url;
}) (my_pics[i]['url'], promises[i] = $.Deferred());
}
$.when.apply($, promises).done(function() {
// add the new images using append()
// when done, call my own layout function
}
... with the array my_pics containing the URLs of the images to load.
Any ideas? :-)
Thanks in advance,
snorri
After adding the new images using append, yield the control to the browser so that the browser can update the ui and then invoke your layout function. You can do so my using a setTimeout with delay set to 0.
$.when.apply($, promises).done(function() {
// add the new images using append()
setTimeout(function () {
// when done, call my own layout function
}, 0);
}
There are many SO threads that explain why this works and is useful.
Here is one good explanation
Also, worth watching is the video by Philip Roberts on browser's event loop.

jQuery .load(function) synchronous

What is the best way to use the jQuery load function synchronously.
I need to load an image but can't execute the next line of code until that image has loaded.
I could loop a variable until the load has completed but was wondering if there was a better way of doing that.
var img = jQuery('<img src="' + url + '"/>').load(function () {
});
//Run code here once img load has comlpeted.
You can also use CallBack function to get Synchronous Behaviour -
var result = $('#main-container').load( 'html/Welcomeform.html',
function () {
if($("textarea").find("#mail-id")){
$("textarea#mail-id").val(email_id);
}
} );
From what I know, the load event will always fire asynchronously, except if the image is already cached (in some browsers). The only reliable solution is to put the code in a callback like you did. However, to make sure the load handler will always be fired in all browsers, even if the image is cached, make sure to add the handler before setting the src property of the image.
var img = jQuery('<img src="' + url + '"/>').load(runner);
function runner() {
//run code here once image is loaded
}
I arrived here looking for a similar solution. From the reads, it is not possible with .load, you need to use an AJAX request, as the question comment points out.
In my case I need to load a html file and I have added a transition to change the content. The old content were showed before the new one after the transition even if I was showing the content inside the load callback.
var main = $("#main");
main.load("about.html", displaySection);
function displaySection () {
main.show('blind');
}
My workaround has been to run the transition that shows the loaded content inside a timeout function with a of 200 for the delay parameter.
var main = $("#main");
main.load("about.html", displaySection);
function displaySection () {
setTimeout(function() {
main.show('blind');
}, 200);
}
The problem could be if the connection is so slow that the new page takes more than 200 ms to load, but in this case I wonder the callback will be launched later on. I don't understand why is not working without the timeout that I feel quite ugly, but it solved my problem ... just in case any other has not given a thought on this possibility.
The callback function in load() will fire once the basic elements of the screen have been retrieved, (the actual html) but doesn't wait for images to finish, you can use this to make it wait.
$('#holder').load(function() {
var imgcount = $('#holder img').length;
$('#holder img').load(function(){
imgcount--;
if (imgcount == 0) {
/* now they're all loaded, let's display them! */
}
});
});

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