JavaScript image.onload generates infinite loop - javascript

I've a situation where I need to work on a user-provided image with two different functions.
Get user input
Process the image and put it back processed
note: code is incomplete and shortened for brevity. Please don't point out the irrelevant.
1. Get user input
var fReader = new FileReader();
fReader.onload = function(e){
image = new Image();
image.onload = function(){
//BEGIN OF RELEVANT SECTION
processOnCanvasAndBack(image, myCallbackToProceed);
};
image.src = e.target.result;
};
fReader.readAsDataURL(src);
2. Process the image and put it back processed
function processOnCanvasAndBack(image) {
var canvas = $('<canvas></canvas>');
canvas.draw(image);
canvas.doSomeStuffLikeRotatingAndColorBalance();
//BEGIN OF RELEVANT SECTION
image.onload = function() {
myCallbackToProceed();
};
image.src = canvas.toDataURL();
}
Problem
The image.onload from 1. calls the function as expected but when I call the second image.src from 2 the first image.onload gets called again, which in turns calls 2 again and.... booooom, infinite loop (console spits too many recursions)
I tried to reset the first call with image.onload = function(){}; in various points, but it doesn't fix the issue (no more recursion, but the functions just stop being called). Right now I'm out of ideas :-(

I would suggest creating two image objects. One for the source image, and a second for the transformed / target image. You avoid mutating existing state and causing infinite loops by repeatedly setting the .src of the same image object in the onload events. Avoid mutating state whenever possible. I would also suggest using the var keyword to define the variables locally instead of in the global scope.
var fReader = new FileReader();
fReader.onload = function(e){
var sourceImage = new Image();
var targetImage = new Image();
sourceImage.onload = function() {
//BEGIN OF RELEVANT SECTION
processOnCanvasAndBack(sourceImage, targetImage, myCallbackToProceed);
}
sourceImage.src = e.target.result;
};
fReader.readAsDataURL(src);
function processOnCanvasAndBack(sourceImage, targetImage, callback) {
var canvas = $('<canvas></canvas>');
canvas.draw(sourceImage);
canvas.doSomeStuffLikeRotatingAndColorBalance();
// BEGIN OF RELEVANT SECTION
targetImage.onload = function() {
callback();
};
targetImage.src = canvas.toDataURL();
}

Related

loop ends when function called

I'm writing what I think is some pretty routine javascript. However I'm having an issue I can't seem to resolve. I'm looping through an array and when it reaches the point of a function being called, the loop breaks. If I take the function out the loops runs to completion. I can't seem to figure it out.
for (i=0;i<response.items.length;i++) {
console.log("cover: ", decodeURIComponent(response.items[i].coverPhotoPath));
previewAlbum(decodeURIComponent(response.items[i].coverPhotoPath));
}
The code for the function being called is...
function previewAlbum(file) {
console.log("preview", file);
var galleryId = "photo";
var gallery = document.getElementById(galleryId);
var img = document.createElement("img");
img.style.cssText = 'width:200px;height:200px;';
img.src = file;
gallery.appendChild(img);
// Using FileReader to display the image content
var reader = new FileReader();
reader.onload = (function(aImg) { return function(e) { aImg.src = e.target.result; }; })(img);
reader.readAsDataURL(file);
}
Each piece of code works on it's own. But when put together the loop breaks. Any ideas?

Memory leak when loading images

I'm using the following code to load and resize images.
$(imagesToProcess.files).each(function (idx, file) {
var img = new Image();
img.onload = function (e) {
//var resized = _resizeImage(e.target);
URL.revokeObjectURL(e.target.src);
};
img.src = URL.createObjectURL(file);
});
This code results in a gigantic memory spike in Chrome, even though I commented out the actual resizing. What am I doing wrong?
This code, which is based on this answer, solved it for me
var fileQueue = [];
var lock = false;
var img = new Image();
img.onload = function (e) {
URL.revokeObjectURL(e.target.src);
lock = false;
};
$(imagesToProcess.files).each(function (idx, file) {
fileQueue.push(file);
});
var processQueue = setInterval(processFile, 250);
function processFile() {
if (fileQueue.length == 0) {
console.log('nothing in queue');
clearInterval(processQueue);
return;
}
if (!lock) {
img.src = URL.createObjectURL(fileQueue.shift());
lock = true;
}
}
Don't use much of anonymous functions instead use reference of the functions.
$(imagesToProcess.files).each(createImage);
function createImage(idx, file){
var img = new Image();
img.onload = imageOnload;
img.src = URL.createObjectURL(file);
}
function imageOnload(e) {
URL.revokeObjectURL(e.target.src);
}
In you code for every image creation one instance of function object is created. That is what causing memory leak in your code.
I ended up solving this memory leak in my program by using an alternate method of jpeg-decoding (using library rather than browser's Image object): https://stackoverflow.com/a/62584068/2441655

Get variable inside load function

I know this might probably been answered many times but I do not know the words to search for to find the answer. So here is the question.
I'm using javascript and I have
function getDataUrl( file ){
var url = URL.createObjectURL( file ),
canvas = document.createElement('canvas'),
ctx = canvas.getContext("2d"),
img = new Image,
dataURL = '';
img.load = function(){
canvas.height = this.height;
canvas.width = this.width;
ctx.drawImage(this, 0, 0);
dataURL = canvas.toDataURL("image/jpeg");
};
img.src = url;
return dataURL;
}
then on the input I have this,
$('#file-input').on('change', function(e){
var dataUrl = getDataUrl( e.target.files[0] );
console.log( dataUrl );
});
But it return empty string (the initial one). I want the base64 data that already generated inside load.
I've try something like this
function getDataUrl( file ){
//... codes
img.load = function(){
//... codes
dataURL = canvas.toDataURL("image/jpeg");
return dataUrl;
};
}
but of course it can't since it return to the load function and not to it's parent function.
How do I get dataUrl value inside load function ?
Thanks
Your code, abbreviated, looks like this:
function getDataUrl() {
dataURL = '';
img.load = function() {
dataURL = 'Hello!';
};
return dataURL;
}
What img.load = function does is nothing except assign a function to img.load, the execution will then just continue with the return statement, meaning that your dataURL variable retains the original value.
You should realize that functions in Javascript van be assigned to variables just like a string or number can. It's doesn't need to be executed.
It may be a bit confusing, it's certainly different from many other languages, but you'll get the hang of it. Be sure to continue reading & learning about javascript.
You probably want to use img.onload, this will execute the function whenever the image is finished loading. But this can be whenever, maybe 1 second, maybe 10, maybe never.
What you need to do, is something like:
$('#file-input').on('change', function(e) {
getDataUrl(e.target.files[0], function(dataUrl) {
console.log(dataUrl);
});
});
function getDataUrl(callback) {
dataURL = '';
img.onload = function() {
callback(dataUrl);
};
return dataURL;
}
Here you pass a function as an argument, an execute that function once the image finished loading, passing dataUrl as a parameter. This is a fairly common pattern in Javascript.
The load event is asynchronous. That means it occurs sometime in the future AFTER your getDataURL() function has completed. Thus, you cannot return the value you want from the getDataURL() function because the image has not loaded yet and the load event handler has not yet been called. So, instead, you need to think asynchronously and program in a way that deals with that. The typical way of doing this is to pass in a callback function that will get called when the data is available and you can pass that data to the callback function.
There are many different ways to code this, but here's one way:
function getDataUrl( file, callback ){
var url = URL.createObjectURL( file ),
canvas = document.createElement('canvas'),
ctx = canvas.getContext("2d"),
img = new Image,
dataURL = '';
img.load = function(){
canvas.height = this.height;
canvas.width = this.width;
ctx.drawImage(this, 0, 0);
dataURL = canvas.toDataURL("image/jpeg");
// pass the dataURL to the callback function
callback(dataURL);
};
img.src = url;
}
getDataUrl("myfile.jpg", function(dataURL) {
// code here to use the dataURL
});

get the result from the FileReader()

I have the following problem.
I have an upload form for multiple files. The upload process goes fine. The problem is related with the FileReader() result. As I am uploading multiple files(images) I need to create corresponding thumbs when the upload is finished.
files = e.target.files;
for (var i = 0; i < files.length; i++) {
//ajax request goes here
var reader = new FileReader();
reader.onload = (function (e) {
var img = document.createElement('img');
img.file = e;
img.className = 'thumbs_';
img.src = e.target.result;
//Every image has a wrapper div with the id 'nDv0','nDv1','nDv2' etc.
document.getElementById('nDv' + i).appendChild(img);
})(e);
reader.readAsDataURL(files[i]);
//request sent
}
If I remove the closures around the anonymous function, i's value will be anything when the for loop exits.
For example, if there are 3 files, i's value will be 3 and the results will be appended to the last wrapper div and images will be displayed.
With closures every image is getting appended to the corresponding div, but the images won't be displayed as the returned result is undefined.
So, how can I append every thumb to its corresponding div?
You has a logic error:
reader.onload = (function (e) {
//onload code
//inside function e is event from outside
})(e);//e is event from upper code
And the i will be equal to files.length because onload handler is async.
So you must change your onload handler to:
reader.onload = function (index) {
var img = document.createElement('img');
img.className = 'thumbs_';//it's right className?
img.src = this.result;//result is dataURL
document.getElementById('nDv' + index).appendChild(img);
}.bind(reader, i);

jQuery variable not being updated

I am using HTML5 file API to get the width of an image.
Its working, and the console.log is outputting the correct amount.
I need to save the value to the variable fileWidth so I can access it outside the function. I have created an empty variable outside the function, and expected it to be updated with the alue inside the function, but whatever I try fails.
Here is my code:
var fileWidth;
var reader = new FileReader;
reader.onload = function() {
var img = new Image;
img.onload = function() {
fileWidth = img.width;
console.log(fileWidth);
};
img.src = reader.result;
};
reader.readAsDataURL($('#submission-file')[0].files[0]);
Can anyone see why the variable isn't being updated?
Edited Code:
var fileWidth;
var reader = new FileReader;
reader.onload = function() {
var img = new Image;
img.src = reader.result;
};
reader.onloadend = function() {
fileWidth = img.width;
};
reader.readAsDataURL($('#submission-file')[0].files[0]);
console.log(fileWidth);
You're setting fileWidth in an asynchronous callback, this method isn't guaranteed to have executed prior to your accessing the variable.
Hint
Try putting an alert in your callback and an alert right before you access the variable. See which alert is presented first.

Categories

Resources