JavaScript Onload property - javascript

I have read some posts regarding the onload property. My understanding is that it is not a method, not an event, event listener, or trigger. It is merely a empty pointer to a function waiting to be assigned. Here is a shortened script. It assigns a image manipulation function upon event then renders the image to the web page. Instead of assign the function to onload which is the pointer to the function. Why can't I just execute the function directly? Am I making any sense here? Thanks
var reader = new FileReader();
reader.onload = function(e) {
var img = document.createElement("img");
img.src = e.target.result;
var canvas = document.createElement("canvas");
......
dataurl = canvas.toDataURL(file.type);
document.getElementById('output').src = dataurl;
}
reader.readAsDataURL(file);

Accordingly to MDN:
The FileReader.onload property contains an event handler executed when
the load event is fired, when content read with readAsArrayBuffer,
readAsBinaryString, readAsDataURL or readAsText is available.
So it's executed after reading content is done, that you're performing by calling reader.readAsDataURL(file).

onload is a field of the object FileReader which handles a reference to the function that you wanna execute when the desired file gets loaded. Essentially, it's the callback that gets called when the event load is triggered. Why using this pattern and not executing the function directly? Because loading files is an asynchronous task (which is initialized with readAsDataURL, readAsBinaryString, readAsArrayBuffer or readAsText).

You can't call the function directly because files load asynchronously. You can convert the FileReader code to return a promise though and then you can use async/await to make the code look like you're directly calling it
funciton readFileAsDataURL(file) {
return new Promise((resolve, reject) => {
const reader = new FileReader();
reader.onload = () => resolve(e.target.result);
reader.onerror = reject;
reader.readAsDataURL(file);
};
}
function loadImage(url) {
return new Promise((resolve, reject) => {
const img = new Image():
img.onload = () => resolve(img);
img.onerror = reject;
img.src = url;
};
}
async function foo() {
const result = await readFileAsDataURL(file);
const img = await loadImage(result);
// you can now use img
......
}

So in essence, OnLoad is an event handler (or reference to the event handler) for FileReader when it is called to further processes data when FileReader is done loading? Is my interpretation correct?

Related

How to manually trigger file reader onerror event?

I'm testing all the scenarios on reading files using new FileReader();
When the read is done, onload() should be triggered.
I have no idea how to trigger onerror() callback
Any idea?
var promise = Promise.create();
var fileReader = new FileReader();
fileReader.onload = function () {
};
fileReader.onerror = function(event) {
// I want to stay here ..........
// Who can tell me how to trigger this onerror callback?
debugger
};
fileReader.readAsText(inputFile);
return promise;
Try simply calling abort:
var promise = Promise.create();
var fileReader = new FileReader();
fileReader.onload = function () {
fileReader.abort()
};
fileReader.onerror = function(event) {
// I want to stay here ..........
// Who can tell me how to trigger this onerror callback?
debugger
};
fileReader.readAsText(inputFile);
return promise;
If that doesn't work you can try a permissions error with something like:
touch testfile
chmod 000 testfile
And open that file from your reader.
Maybe what you're trying to find out is how to use Async functions:
async function FileReader(){
// your code for the file reading
return;
};
then use await in your onload() function to wait for it to return:
async function onload(){
await firstFunction(); // This waits for fileReader() function to finish...
// do what you want to show when fileReading is done
};
Hope this helps!
You can run your code in debugger mode and add a breakpoint at line "fileReader.readAsText(inputFile)". When the debugger stops, you rename manually the file in your filesystem. FileReader then throws an error.
The accepted answer doesn't work in my environment.

File Reader is slow and not set state on React

When converting a. pdf file to Base64, I need to be filled in my state, but for some reason my conversion is slower than my setstate, which when being seted is always empty.
My code
async transformBase64(inputFile) {
return new Promise((resolve, reject) => {
var fileReader = new FileReader();
fileReader.readAsDataURL(inputFile)
if (fileReader.result != undefined){
resolve(this.setState({ Base64: fileReader.result }), () => {});
}else{
reject("Err")
}
});
}
What can I do to solve my problem?
It looks like you're not setting up an onload callback for your fileReader. This is needed to correctly signal back to your application that file data has loaded and is ready, seeing that the FileReader API is asynchronous.
Consider apply the following changes to your code to resolve your problem:
async transformBase64(inputFile) {
return new Promise((resolve, reject) => {
var fileReader = new FileReader();
// If error occurs, reject the promise
fileReader.onerror = () => {
reject("Err")
}
// Define an onload handler that's called when file loaded
fileReader.onload = () => {
// File data loaded, so proceed to call setState
if (fileReader.result != undefined){
resolve(this.setState({
Base64: fileReader.result
}), () => {});
}else{
reject("Err")
}
}
fileReader.readAsDataURL(inputFile)
});
}

loading an image on web browser using Promise()

I'am trying to learn how to make SuperMario in JavaScript from here
Can someone explain flow of the below function LoadImage ?
function loadImage(url) {
return new Promise(resolve => {
const image = new Image();
image.addEventListener('load', () => {
resolve(image);
});
image.src = url;
});
}
const canvas = document.getElementById('screen');
const context = canvas.getContext('2d');
context.fillRect(0,0,50,50);
loadImage('/img/tiles.png')
.then(image=>{
context.drawImage(image,0,0); // the function LoadImage returns a Promise with image object(which is a constant)
// as parameter and if the promise is fulfilled then the image is drawn.
/
});
EDIT
I do understand how to use => operator.
Which is used to make length of functions smaller.
image.addEventListener('load', () => {
resolve(image);
the above line means that the promise is fulfilled when image is loaded.
So does this mean that the following line is executed and then the event listener is waiting for the image to be downloaded in client browser ?
image.scr = url;
The flow of the below function is a little fuzzy to me
function loadImage(url) {
return new Promise(resolve => {
const image = new Image();
image.addEventListener('load', () => {
resolve(image);
});
image.src = url;
});
EDIT 2:
Okay, this was a stupid post. And yup as the IMAGE from url is loaded in the image object then Event listener fires up the resolve().
The code you are showing introduces an asynchronous primitive, Promise, which can be passed around and used to access a resource that hasn't been populated yet.
In this case, you want an Image that is fully loaded and has image data that you can use. However, you can't access the image data until a network request finishes that would fetch the image data.
For example, this won't work:
const img = new Image();
img.src = "example.com/house.jpg";
ctx.drawImage(img, 0, 0); // img isn't done loading yet
Instead, we have to wait until the loading is done. There are a lot of ways to do that but the most common conventions are to use, callbacks, Promises, or async/await.
The method that you have shown combines callbacks and Promises (out of necessity).
Let's break down the code:
/**
* Load an image from a given URL
* #param {String} url The URL of the image resource
* #returns {Promise<Image>} The loaded image
*/
function loadImage(url) {
/*
* We are going to return a Promise which, when we .then
* will give us an Image that should be fully loaded
*/
return new Promise(resolve => {
/*
* Create the image that we are going to use to
* to hold the resource
*/
const image = new Image();
/*
* The Image API deals in even listeners and callbacks
* we attach a listener for the "load" event which fires
* when the Image has finished the network request and
* populated the Image with data
*/
image.addEventListener('load', () => {
/*
* You have to manually tell the Promise that you are
* done dealing with asynchronous stuff and you are ready
* for it to give anything that attached a callback
* through .then a realized value. We do that by calling
* resolve and passing it the realized value
*/
resolve(image);
});
/*
* Setting the Image.src is what starts the networking process
* to populate an image. After you set it, the browser fires
* a request to get the resource. We attached a load listener
* which will be called once the request finishes and we have
* image data
*/
image.src = url;
});
}
/*
* To use this we call the loadImage function and call .then
* on the Promise that it returns, passing a function that we
* want to receive the realized Image
*/
loadImage("example.com/house.jpg").then(houseImage => {
ctx.drawImage(houseImage, 0, 0);
});
In all honesty though, the loadImage function could be a little bit more robust since it doesn't handle errors right now. Consider the following enhancement:
const loadImage = (url) => new Promise((resolve, reject) => {
const img = new Image();
img.addEventListener('load', () => resolve(img));
img.addEventListener('error', (err) => reject(err));
img.src = url;
});
loadImage("example.com/house.jpg")
.then(img => console.log(`w: ${img.width} | h: ${img.height}`))
.catch(err => console.error(err));
A compressed/short example:
let url = "https://example.com/image.png";
let img = new Image();
await new Promise(r => {img.onload=r; img.src=url});
// now do something with img

Variable returning pre-defined value due to async function late execution and completion [duplicate]

This question already has answers here:
How do I return the response from an asynchronous call?
(41 answers)
Closed 6 years ago.
I have this javascript code. I am converting blob to base64 here. The reader.onloadend function when called starts processing asynchronously. So console(reader.result) which should print after the function, prints before the function completion and gives wrong value(value before the change in function). I want to use the changed value of reader.result after completion of async onload function.
var reader = new window.FileReader();
reader.readAsDataURL(array);
reader.onloadend = kk=function() {
base64data = reader.result;
//console.log(base64data );
//document.write(base64data);
//document.getElementById('display1').setAttribute('src',base64data);
return base64data
}
console(reader.result)
Why not to call another method from the onloadend handler:
var reader = new window.FileReader();
reader.readAsDataURL(array);
reader.onloadend = kk=function() {
base64data = reader.result;
// sync way - execution will start before the rest of the handler
loadEndCtd(base64data);
// or async way - execution will start after handler is done
// setTimeout(function(){ loadEndCtd(base64data); },0);
return base64data
}
var loadEndCtd = function( data ){
console.log(data);
}
Just do what you want to do when the result is there, i.e. execute the code that needs the result from within the callback function:
var reader = new window.FileReader();
reader.readAsDataURL(array);
reader.onloadend = kk= processResult;
function processResult() {
var base64data = reader.result;
// all processing should come here or be launched from here.
}

JS Wait for few events in a function to complete

I have a function which uploads images and displays it on the page. How should I do to wait for the function to complete?
$("#images").change(function(){
updateArray(this);
addImages();
});
function updateArray(inp)
{
for(var i=0,file; file = inp.files[i]; i++)
{
var reader = new FileReader();
reader.onload = function (evt)
{
images[images.length] = evt.target.result;
}
reader.readAsDataURL(file);
}
}
I want to call addImages() after all events in updateArray() are completed
I would use jQuery's deferred/promises here. This will allow you to wait until each onload is complete, and then trigger a callback.
Here's an example:
function updateArray(inp)
{
var promises = [];
for(var i=0,file; file = inp.files[i]; i++)
{
var reader = new FileReader(),
d = new $.Deferred();
promises.push(d);
// Make sure we "capture" the correct 'd'
(function(d){
reader.onload = function (evt)
{
images.push(evt.target.result);
d.resolve();
}
}(d));
reader.readAsDataURL(file);
}
return $.when.apply($, promises);
}
This will create a new deferred object for each FileReader. When each one finishes, its respective deferred will be resolved. The $.when is used to combine all of the deferreds into one. So, in your .change(), you can simply do this:
$("#images").change(function(){
updateArray(this).done(function(){
addImages();
});
});
P.S. You can also pass parameters to resolve() to be used in the .done() callback. So, instead of appending to the images array, you can do d.resolve(evt.target.result);.
(function(d){
reader.onload = function (evt)
{
d.resolve(evt.target.result);
}
}(d));
Then in .done(), you can do:
$("#images").change(function(){
updateArray(this).done(function(){
// Each '.resolve' added a new parameter to here, let's get them all
images = [].slice.call(arguments, 0);
addImages();
});
});
There are a couple of ways that you can approach this, however if you're using jQuery (which you appear to be) I'd recommend looking into promises (see: http://api.jquery.com/promise/).
They are a more modern paradigm for deferred callbacks which have some semantic processing for executing functions based on successful completion, failure, and those which will always process.

Categories