0

I am not able to access a class variable which is set inside FileReader onloadend method.

Here is my code:

analyzeData(){
let file = this.fileRestful[0];
let fileReader: FileReader = new FileReader();
fileReader.onloadend = () => {
  this.fileContent= fileReader.result as string;
}
fileReader.readAsText(file);
console.log(this.fileContent)

}

I have tried 3 methods till now but none worked. Following are they:

  1. fileReader.onloadend = () => { this.fileContent= fileReader.result as string; }
  2. fileReader.onloadend = function(e) = { this.fileContent= fileReader.result as string; }.bind(this)

  3. let self = this; fileReader.onloadend = function(e) = { self.fileContent= fileReader.result as string; }

I have already seen most of the solution but none worked for me. Please let me know where I am making error. Thank you.

1 Answers1

2

I don't think that the problem here is caused by this keyword. Your this points just to the right place, but there is nothing assigned to this.fileContent variable.

this.fileContent is set in an asynchronous block of code. Your assignment will be executed after your console.log, because fileReader.onloadend is just a listener, similar to onclick listener in the DOM.

fileReader.onloadend = () => {
  this.fileContent= fileReader.result as string; // this code will be executed some time in the future
}
fileReader.readAsText(file);
console.log(this.fileContent); // this code will be executed first

In other words, in the first line you add an event listener to the loadend event. Your arrow function will be executed when loadend happens, not immediately. When will it happen - we don't know, maybe in three years, maybe in 0.0002 seconds but certainly after the rest of your code is executed.

Solution

Your console.log will reflect the reality when you move it to the asynchronous part of your code, like this:

fileReader.onloadend = () => {
  this.fileContent= fileReader.result as string; // this code will be executed some time in the future
  console.log(this.fileContent); // and after that you do your console.log

}
fileReader.readAsText(file);

setTimeout, on* handlers, code inside addEventListener are the examples where we need to always remember about the time of execution of our code. It can be executed three seconds or three years after the synchronous code. A canonical resource that I would recommend to read is this section on MDN. It's a lot, but it's essential to understand in order to write js code.

ganqqwerty
  • 1,894
  • 2
  • 23
  • 36
  • Hi @ganqqwery: as you mentioned console.log(this.fileContent); always prints it. My problem is even though I wait I am not able to access fileContent outside the onloadend function. – Kanhaiya Choudhary Apr 20 '20 at 12:24
  • There is no way to access `fileContent` outside the `onloadend` function, because the javascript engine doesn't wait till `onloadend` finishes. The code after `onloadend` function declaration will be executed after the `onloadend` function. – ganqqwerty Apr 20 '20 at 12:29
  • what I meant is something like: https://stackblitz.com/edit/angular-file-read. Here you can access it. I may be wrong. But it doesn't work for me. – Kanhaiya Choudhary Apr 20 '20 at 12:33
  • There the situation is different. In angular core there is an asynchronous infinite loop that always checks if the value of the variable in the templated has changed or not, it's called Change Detection. That's why it shows you the content. Imagine that you have your console.log inside never ending `setInterval` and it continuously prints the value. – ganqqwerty Apr 20 '20 at 12:37
  • Ok. So what options I have now? I want to take fileContent and call another REST api whose object is in class constructor. But when I use the REST service with "this" within onloaded function it always comes as null. – Kanhaiya Choudhary Apr 20 '20 at 12:40
  • The easiest thing that you can do is to move your rest service call inside the `onloadend`. In a more mature angular code we would use an RxJS approach. We would create an observable out of `onloadend` event and `switchMap`ped from it to the REST call. – ganqqwerty Apr 20 '20 at 12:54
  • About creating an observable or promise out of `onloadend` - take a look at this question: https://stackoverflow.com/questions/46513123/angular-return-observable-es6-promise-from-filereader – ganqqwerty Apr 20 '20 at 13:10
  • Thank you @ganqqwery. Let me check it. – Kanhaiya Choudhary Apr 21 '20 at 05:05