0

A rookie question, but I am trying to understand how PapaParse (or anything else, for that matter) uses callbacks. When I use the following code:

Here is the revised full code:

<html>
    <head>
    <script src="papaparse.js"></script>
    <script src="drawTable.js"></script> 

</head>
<body>  
 <label>Load CSV file: </label><input type="file" id="fileInputCSV" /><br/>

    Results: 
    <table id="outputTable" border=1 px>
        <tbody id="objTable"></tbody>
    </table>


  <script type="text/javascript">      
 var csvData = [];  
 function GetCSV(doneCallback) {
   var fileInput = document.getElementById('fileInputCSV'); 
       Papa.parse(fileInput.files[0], {
       header: true,
       skipEmptyLines: true,

       complete: function(results) {
       console.log('Done.');
       doneCallback(results);
    }
   });
 }
}

GetCSV(function(csvData) {
    console.table(csvData.data);
   drawTable(csvData.data, "objTable");
});
    </script>
    </body>
</html>

I have to reload the page to get the console.table, and the object generated by PapaParse is unavailable outside the PapaParse function.

I know this has been asked in other forms, but always has been answered in jQuery. Is there a solution is plain JavaScript? I really need not just to be able to display the data, but actually to be able to use it.

PapaParse itself is noticeably quiet on such a basic use of their program....

Thanks!

Tyler330
  • 395
  • 2
  • 6
  • 15

2 Answers2

1

All code that wants to use an asynchronous result reliably as soon as it is available MUST be either located inside the completion callback function or must be in a function that is called from that callback function.

This is because an asynchronous response happens sometime LATER at an indeterminate time. The ONLY place in your code where you can know exactly when the result is ready is inside the completion callback itself. It is a very bad practice to try to stuff an asynchronous result in some higher scoped variable and then use it later because your other code will have no idea when that value is actually available - generally leading to all sorts of timing issues.

This is a different type of programming than sequential programming. You don't just call getCSV() and then use the results from that on the next line of code. The results won't be available until some time later.

If you wanted to know when the results to getCSV() were available, you would construct it so that it takes a callback:

function GetCSV(doneCallback) {
   var fileInput = document.getElementById('fileInputCSV'); 
   Papa.parse(fileInput.files[0], {
       header: true,
       skipEmptyLines: true,

       complete: function(results) {
           console.log('Done.');
           doneCallback(results);
        }
    });
 }

Then, you can call it like this and consume the results inside the callback:

GetCSV(function(csvData) {
    // use the data here
     drawTable(csvData.data, "objTable");
});
jfriend00
  • 683,504
  • 96
  • 985
  • 979
  • I can think of contrived examples where your first assertion is incorrect – Billy Moon Nov 23 '15 at 22:22
  • @BillyMoon - Contrived examples? Really? That's what you're objecting to? In any case, I've tweaked the wording in that first paragraph. – jfriend00 Nov 23 '15 at 22:27
  • Thanks! But I still only get output if I reload the page. – Tyler330 Nov 23 '15 at 22:27
  • @Tyler330 - I don't understand that part of the issue. Do you mean that when the page first loads, your code doesn't work? And it only works after a reload? That is usually the sign of a timing problem in your code where your code only works after results are cached the first time, but you don't disclose enough info about your code, page and the problem for us to understand that part of the issue.. – jfriend00 Nov 23 '15 at 22:30
  • Yes, I have to reload the page before I get output either in the DOM or in the console. Here is the html:

    Results:
    – Tyler330 Nov 23 '15 at 22:33
  • @Tyler330 - multiline code is not readable in comments. Please use the "edit" button to add that code to your question. – jfriend00 Nov 23 '15 at 22:35
  • @Tyler330 - Where in the page are you calling `GetCSV()`? That MUST be called after the page has already been loaded and after the input control fileInputCSV has gotten its value. – jfriend00 Nov 23 '15 at 22:38
  • I am calling it at after the GetCSV function. – Tyler330 Nov 23 '15 at 22:49
  • @Tyler330 - did you read and understand my answer? You can't just call `drawTable()` right after `GetCSV()`. My answer shows you a proper way to do that. – jfriend00 Nov 23 '15 at 22:53
  • No, I guess I didn't understand. Where should I place it? – Tyler330 Nov 23 '15 at 22:56
  • @Tyler330 - EXACTLY how it is shown in my answer with the modified `getCSV()` I show. – jfriend00 Nov 23 '15 at 22:57
  • I thought that that is what I did. – Tyler330 Nov 23 '15 at 22:57
  • @Tyler330 - Well this is structurally how you should do it. If it is still not working, then there must be some other problem that you have not disclosed info about. Are you seeing any errors in the debug console that would be a clue about another problem. – jfriend00 Nov 23 '15 at 22:59
  • When I first load it, I get "TypeError: Stream is null", which suggests that GetCSV is being called before the file has been input....? After the reload there are no error messages. – Tyler330 Nov 23 '15 at 23:04
  • Why are you calling this when the page is first loaded? Shouldn't you give the user a chance to specify a file in fileInputCSV and then AFTER that has been specified by the user, you then call `getCSV()`? – jfriend00 Nov 23 '15 at 23:06
  • Even if I move the javascript, I get the same result. – Tyler330 Nov 23 '15 at 23:11
  • @Tyler330 - Move it to where? You probably want it inside an event handler. – jfriend00 Nov 23 '15 at 23:17
  • @Tyler330 - which event depends upon exactly when you want the function to run. If you want it to run when the user presses a button, then you would use a click event for that button. – jfriend00 Nov 23 '15 at 23:21
  • I wanted it to run as soon as the file was loaded, so I will give that a shot. Thank you for your time and patience! – Tyler330 Nov 23 '15 at 23:23
0

If you add some console.log statements in your program you will see that MakeTable is getting called before the complete callback in the Papa.parse function. This is to be expected in Javascript, which does not "wait" for async operations to complete before proceeding. What this means is that when you are working with callbacks you can't write functions that "return" when they are done. Instead, you need to write functions that call a callback when they are done.

One way to fix your program is to add a callback parameter to your GetCSV function

function getCSV(onDone) {
    Papa.parse(the_file, {
        complete: function(results) {
            consoel.log("got data")
            onDone(results); //pass our csv to the callback instead
                             //of setting a global variable.
        }
    });
}

getCSV(function(csv){
   makeTable(csv);
});
hugomg
  • 68,213
  • 24
  • 160
  • 246