1

I'm trying to upload a local .json file into my web application. I have already come to a point where I can display the file in the dev tools, but I cannot make it available for further use. I suspect the problem to be in the way I handle (or don't handle) the asynchronous behaviour of the file reader.

The whole things works in an Angularjs (1.7) environment, hence the syntax of the snippet. The intended use is, to display the read in data on an openlayers map.

    this.jsonSelected = function(newFile) {
        let reader = new FileReader();
        let result = 'empty';
        reader.readAsText(newFile);
        reader.onload = function(e) {
            result = e.target.result;
            console.log('in onload', result);
            this.result = e.target.result;
        };
        console.log(this.result);
    };

    this.test = function() {
        console.log(this.file);
    }

I would expect the code the log out the file content twice. Once on "console.log ('in onload', result);" and another time on "console.log (this. result);". The first one works as expected. but for some reason the second one wont. Also the order in the console is flipped, console.log (this. result) comes before the inload log as you can see in this screenshot of the console.

I have tried several variations, flipping around the names, changing the this. and so on, but to no avail. That is why I believe I'm messing up the handling of the asynchronous data. Also the screenshot, more specifically the linies from the logs, indicate some sort of timing problem.

jdoe
  • 99
  • 3
  • 14
  • If the app is a client side app, sorry, your browser won't be able to save it locally. – Praveen Kumar Purushothaman Apr 03 '19 at 10:53
  • I dont want to save the data locally, it is exactly the other way around. The .json file is local and i want to make it's content available to the browser. – jdoe Apr 03 '19 at 11:11
  • The console order is as would be expected. Setting an onload function doesn't make the code wait for the load to complete, it carries on and logs empty. Some time later the load will complete and the onload function will run. Just like other asynchronous operations such as xhr is meaningless to try to use the result before it has loaded, so all handling of it should be called from the onload function. As with xhr it is also good practice to set the onload before starting the read to avoid any chance of the onload not being called if a load completes sooner than expected. – Mike Apr 03 '19 at 11:19

2 Answers2

1

After some further research and some trial and error i got things to work out. Code now looks like this:

function readFile(newFile) {
    return $q(function(resolve, reject) {
        let reader = new FileReader();
        reader.onload = function(event) {
            resolve(event.target.result);
        };
        reader.onerror = function(event) {
            reject(event);
        };
        reader.readAsText(newFile);
    });
}

this.jsonSelected = function(newFile) {
    readFile(newFile).then(function(data) {
        console.log(data);
        set$scopeFile(data);
    }, function(errData) {

    });
};

Thank you Ben, you brought me on the right track, I was so focussed on finding a way to deal with the asynchronous parts, I simply overlooked the scoping issues. If anyone has some "fool proof" docs on asynchronous JS I would really appreciate it, since I'm having trouble wrapping my head around this topic.

I have used angularjs' built in $q service to wrap to whole reading process into promise. Which then can by chained with .then.

jdoe
  • 99
  • 3
  • 14
0

This is a classical async + scoping issue...

First you use the wrong scope. the this inside reader.onload isn't the same this as the one in console.log(this.result);.
You should either bind the onload or use arrow functions.

Next, the onload is async, so it will run AFTER console.log(this.result);.
One way of handling this is to use Promises ($q in AngularJS). Something like:

const deferred = $q.defer(); 
deferred.then((data) => { console.log('result', data); });
reader.onload = function(e) {
     result = e.target.result;
     console.log('in onload', result);
     deferred.promise.resolve(e.target.result);
};
ben
  • 3,558
  • 1
  • 15
  • 28
  • thank you for your quick answer. Unfortonatly im still not able to make things work. I Used your code, but the console throws an error : `Uncaught TypeError: deferred.then is not a function` – jdoe Apr 03 '19 at 13:14
  • try `deferred.promise.then` – ben Apr 05 '19 at 09:22