0

I have an old function that is no longer functional. According to the console window, synchronous mode is no longer permitted for use. How would I convert this to use asynchronous mode and deliver the data out?

var loadfile = function (filename, filetype) // Reads a file and returns it's contents as a string.
{filetype = filetype || 'text' // Assume text if no filetype is passed in
 var reader = new XMLHttpRequest() || new ActiveXObject("Microsoft.XMLHTTP"); // Provide a fallback for IE
     reader.responseType = filetype; // Prepare to read the proper type of data
     reader.open("GET", filename, false); // Target our local filename and prepare for synchronous read
     reader.send(); // Begin the read
 return reader.responseText; // Return the data as expected
};

I know I could force this to operate by removing line 4, but then I get XML Errors and warnings about the depreciated synchronous mode being employed. Warnings that could become 'no longer supported' errors that would block the program run months from now.

Also, I can switch to asynchronous mode by changing line 5's false to true, but then there's no data being passed out as the last line is invoked immediately. I could involve reader.onloadend() to process the data, but a return invoked there just casts the read data into the void when I need it to be passed back to the caller of loadfile().

Ergo, I'm stuck. What am I missing here?

EDIT: Adding a potential async version here and pointing out how it doesn't work.

var loadfile = function (filename, filetype)
{var output = '';
 filetype = filetype || 'text';
 var reader = new XMLHttpRequest() || new ActiveXObject("Microsoft.XMLHTTP");
     reader.responseType = 'text';
     reader.open('GET', filename);
     reader.onloadend = function () {output = reader.responseText;}
     reader.send();
 while (output === '') {}
 return output;
};

Naturally this has several issues. First, the scope of reader.onloadend() makes it's copy of output separate from the one in loadfile(). So the retrieved data just disappears. We could return it instead of assigning it to a local variable, but onloadend fires itself (as events do) and the returned data is sent into the void. Unrecoverable. Second, even if we could change loadfile()'s version of output from within the onloadend() function as per passing by reference in C++, the while loop that waits for the output variable to change would (because JavaScript is a single process thread) lock up the system, running an infinite loop and not allowing any changes to occur.

Ergo, still stuck. Yes, we could output to window.name or console.log or document.write, but none of these options allows loadfile() to return the data acquired from XHMLHttpRequest/filename.

At this point I'm stuck with the depreciated synchronous xhr, and XML Parsing Errors as I am retrieving raw text, not XML.

EDIT SECUNDUS: I finessed the script to default to synchronous mode, and managed to silence the errors (but not the initial warning about using a depreciated method...thankfully that doesn't spam warnings on every usage). Needless to say using async/true on this function will not get you anywhere, but async/false does work....for now. Sharing in case anyone else needs this particular functionality.

var loadfile = function (filename, async)
{if ("undefined" === typeof(async)) {async = false;}
 var reader = new XMLHttpRequest() || new ActiveXObject("Microsoft.XMLHTTP");
     reader.open('GET', filename, async);
     reader.onloadend = function () {return reader.responseText;}; // Lost to /dev/null while the program executes without this data.  Unacceptable!
     reader.overrideMimeType('text/plain');
     try
     {reader.send();}
     catch (e)
     {return null;}
 if (!async) {return reader.responseText;}
 //TODO: Find some way to delay execution without generating an infinite loop (good luck - javascript is not multithreaded so we cannot use a while loop for this)
 //TODO: Extract reader.responseText from within reader.onloadend() (probably by invoking a global temp object to shift the data out of onloadend()).
};
Aaron Nichols
  • 131
  • 10

2 Answers2

0

in order to birng your function up to date, you should encapsulate it into a Promise. Here is an older post explaining:

How to promisify native XHR

With callback

function makeRequest (method, url, done) {
  var xhr = new XMLHttpRequest();
  xhr.open(method, url);
  xhr.onload = function () {
    done(null, xhr.response);
  };
  xhr.onerror = function () {
    done(xhr.response);
  };
  xhr.send();
}

// And we'd call it as such:

makeRequest('GET', 'http://example.com', function (err, datums) {
  if (err) { throw err; }
  console.log(datums);
});
Community
  • 1
  • 1
JeanSaigne
  • 323
  • 1
  • 10
  • This is going to take some heavy rewriting, it looks like. returning a Promise instead of the actual data extracted from the file (as the code using this function would reasonably expect) is potentially a long and dark rabbit hole just to get functionality again, if I am reading this Promise material right. – Aaron Nichols Feb 28 '17 at 22:54
  • The link i gave you explain 2 different solutions, Promise and Callback. I think you are looking for the callback solution. – JeanSaigne Feb 28 '17 at 23:08
  • Again we end up in the same space with returning from a subfunction being cast into the void. Console.log(datums) lets you see the data, but returning it there bounces it into space where no one is listening for that string. This leads me to the same runaround I mentioned in the initial question: " I could involve reader.onloadend() to process the data, but a return invoked there just casts the read data into the void when I need it to be passed back to the caller of loadfile()." – Aaron Nichols Feb 28 '17 at 23:23
0

Promises can help you make your asynchronous code more manageable, but it is not required.

In your case, you could simply use event listeners.

var xhr = new XMLHttpRequest() || new ActiveXObject("Microsoft.XMLHTTP");
xhr.responseType = 'text';
xhr.open('GET', 'https://jsonplaceholder.typicode.com/posts/1');
xhr.onload = function(){
    var res = JSON.parse(this.responseText);
    // do something with the response
    document.write('<h1>' + res.title + '</h1>');
    document.write('<p>' + res.body + '</p>');
}
xhr.send();

In other words, you need to refactor your code to remove the need to call for a function that returns something (or, simply, your function should call the rest of the code that requires this xhr through an event handler).

You can read more about the XHR requests and their possible results, events, etc. here.

davidwebca
  • 435
  • 5
  • 11
  • The problem here is you can sit back and push the resulting data string to a webpage, using document.write (ugly code there), plus it doesn't meet the requirement of passing the data back to the caller of loadfile(). Ergo a function call of loadfile('foo.txt') will get back nothing, and therefore execution breaks. – Aaron Nichols Feb 28 '17 at 22:52
  • Well. There's no way of keeping the code "as-is" without refactoring a bit, right? That's what the question is about anyway. And document.write (ugly code?) is to show an example. Thanks. – davidwebca Feb 28 '17 at 23:05
  • Sorry if that came out insulting. The whole point of the function is to return a string containing the contents of the file. Shoving to document.write (where the data is not going), does not solve the problem. – Aaron Nichols Feb 28 '17 at 23:07
  • It was not meant to solve the problem. I wanted to show somes lines as an example of something to do with the response. – davidwebca Feb 28 '17 at 23:09