Chris Chris - 3 months ago 30
Ajax Question

Open local files in JavaScript

I'm new to JavaScript. I found an example to open local files with javascript on StackOverflow. After some googling, I'm able to set my Chrome to allow reading local files, and I am then able to run that example. However, I want to return the string

allText
and use it later in my script. But the string become
undefined
outside
readTextFile()
.

There is a similar question here. It seems like it has something to do with the asynchronous feature of
AJAX
. I can barely the understand the jargons at the moment. I just don't see why in this post the third parameter of
XMLHttpRequest.open()
is set to be
true
.

Anyway, below is my current code. I want to use
allText
outside function
readTextFile()
.

<!DOCTYPE html>
<html>
<script>
function readTextFile(file)
{
var allText;
var rawFile = new XMLHttpRequest();
rawFile.open("GET", file, false);
rawFile.onreadystatechange = function ()
{
if(rawFile.readyState === 4)
{
if(rawFile.status === 200 || rawFile.status == 0)
{
var allText = rawFile.responseText;
alert(allText);
}
}
}
rawFile.send(null);
return allText; // this is the part that goes wrong I think
}

t = readTextFile("foo.file");
document.write(t) // print out "undeifned" instead of the correct answer

</script>
</html>

Answer

This is actually most likely a scope issue. Because you're setting allText asynchronously, it's not available immediately after the function returns. In addition, you're reinitializing allText within a function, which messes with the scope of the return regardless.

rawFile.onreadystatechange is executed after the function returns. You can either move the execution into the XHR callback, or wrap the function in a promise, which would still require you modify your control flow a bit.

Move the document.write:

<!DOCTYPE html>
<html>
    <script>
        function readTextFile(file)
        {   
            var allText;
            var rawFile = new XMLHttpRequest();
            rawFile.open("GET", file);
            rawFile.onreadystatechange = function ()
            {
                if(rawFile.readyState === 4)
                {
                    if(rawFile.status === 200 || rawFile.status == 0)
                    {
                        allText = rawFile.responseText;
                        document.write(allText);
                    }
                }
            }
            rawFile.send(null);
        }

        readTextFile("foo.file");

    </script>
</html>

Promisified:

function readTextFile( file ) {
  return new Promise( function ( fulfill, reject ) {

    var allText;
    var rawFile = new XMLHttpRequest();
    rawFile.open( "GET", file );
    rawFile.onreadystatechange = function () {
      if ( rawFile.readyState === 4 ) {
        if ( rawFile.status === 200 || rawFile.status == 0 ) {
          fulfill( rawFile.responseText )
        }
      }
    }
    rawFile.send( null );
  } );
}
readTextFile( "foo.file" )
  .then( function ( t ) {
    document.write( t );
  } );

Both of these will ensure that your script doesn't attempt to use allText until it's been returned by the XHR request.

Though as Santiago Hernández pointed out, the XHR request is synchronous, and the scope issue was of a different nature than I first assumed. The problem lies in redeclaring the variable within the function, resulting in the one returned to be undefined.

Comments