最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

javascript - Callback function after reading multiple files - Stack Overflow

programmeradmin1浏览0评论

I am doing something similar to /

What I'm doing is Im reading the contents of the selected files one at a time to validate that their lines pass some regex test. After done validating all files, I need to update (enable / disable) some buttons accordingly hence the call back function

Is it possible to have a call back function which will do something after everything is read?

HTML:

<input type="file" id="files" name="files[]" multiple />

Javascipt:

<script>
  function handleFileSelect(evt) {
    var files = evt.target.files; // FileList object

    // files is a FileList of File objects. List some properties.
    var validArray = [];
    for (var i = 0, f; f = files[i]; i++) {
        //Create new file reader
        var r = new FileReader();
        //On load call
        r.onload = (function (f) {
            return function (e) {
                var contents = e.target.result;
                var lines = contents.split('\n');
                
                for(var i=0; i<lines.length; i++){
                    //Validate regex of line here
                    //If line does not pass, append file name to validArray and break  
                }
                                
            };
        })(f);
        r.readAsText(f);
    }
  }

  document.getElementById('files').addEventListener('change', handleFileSelect, false);
</script>

I am doing something similar to http://www.html5rocks./en/tutorials/file/dndfiles/

What I'm doing is Im reading the contents of the selected files one at a time to validate that their lines pass some regex test. After done validating all files, I need to update (enable / disable) some buttons accordingly hence the call back function

Is it possible to have a call back function which will do something after everything is read?

HTML:

<input type="file" id="files" name="files[]" multiple />

Javascipt:

<script>
  function handleFileSelect(evt) {
    var files = evt.target.files; // FileList object

    // files is a FileList of File objects. List some properties.
    var validArray = [];
    for (var i = 0, f; f = files[i]; i++) {
        //Create new file reader
        var r = new FileReader();
        //On load call
        r.onload = (function (f) {
            return function (e) {
                var contents = e.target.result;
                var lines = contents.split('\n');
                
                for(var i=0; i<lines.length; i++){
                    //Validate regex of line here
                    //If line does not pass, append file name to validArray and break  
                }
                                
            };
        })(f);
        r.readAsText(f);
    }
  }

  document.getElementById('files').addEventListener('change', handleFileSelect, false);
</script>
Share Improve this question edited Jun 20, 2020 at 9:12 CommunityBot 11 silver badge asked Dec 6, 2012 at 16:52 Omar WagihOmar Wagih 8,7447 gold badges64 silver badges75 bronze badges 2
  • 1 The code does not to what you think it does. Every time you select a file, the ul is cleared and rewritten with the full list of files, which is kept in the output array (which is exactly the array you are looking for maybe). What is that you are trying to achieve exactly? – Tallmaris Commented Dec 6, 2012 at 17:01
  • Hmm. maybe I simplified my example too much. What I'm doing is Im reading the contents of the selected files one at a time to validate that their lines pass some regex test. After done validating all files, I need to update (enable / disable) some buttons accordingly hence the call back function I will update the question with the details – Omar Wagih Commented Dec 6, 2012 at 17:23
Add a ment  | 

4 Answers 4

Reset to default 5

Came here looking for a similar answer. I wanted to call a function after all files were loaded and processed. The solution provided by @Snuffleupagus did not work for me because the function was called after all the files were read, but before they had finished being processed in the onload function. I found a solution around this as follows (not sure if it is the 'cleanest' but it works for me).

var processedCount=0; // global variable
var totalFiles = 0; // global variable

function handleFileSelect(evt) {
  var files = evt.target.files; // FileList object

  totalFiles = files.length; // important

  // files is a FileList of File objects. List some properties.
  for (var i = 0, f; f = files[i]; i++) {
    //Create new file reader
    var r = new FileReader();
    //On load call
    r.onload = (function(theFile){
        return function(){
          onLoadHandler(this,theFile);
          onLoadEndHandler();
       };
    })(f);
    r.readAsText(f);
  }
}

function onLoadEndHandler(){
  processedCount++;
  if(processedCount == totalFiles){ 
    // do whatever - this code will run after everything has been loaded and processed
  }
}

I tried to use r.onloadend but it was called too soon. I believe because my function 'onLoadHandler' takes a few seconds to process each file and onloadend is called when the file is done being loaded but before the code within 'onload' has finished running.

Absolutely. Callbacks are just passed as any other normal argument would be, so we'll end up adding another argument to handleFileSelect and changing the event listener to an anonymous function that calls handleFileSelect with the extra argument.

I set up a fiddle to give you a quick working demo.

function handleFileSelect(evt, cb) {
    var files = evt.target.files; // FileList object

    // files is a FileList of File objects. List some properties.
    var output = [];
    for (var i = 0, f; f = files[i]; i++) {
      output.push('<li><strong>'+ escape(f.name) + '</strong>');
    }
    document.getElementById('list').innerHTML = '<ul>' + output.join('') + '</ul>';
    if(cb) cb();
}
document.getElementById('files').addEventListener('change', function(e){handleFileSelect(e, function(){alert('all done');})}, false);​

Breaking it down - added an extra argument to handleFileSelect and at the end added if(cb) cb();. That just checks to see if cb exists, if it does, run it as a function.

Then when we go to bind the event handler instead of passing a reference to handleFileSelect we use an anonymous function - this lets us pass our extra argument.

The anonymous function inside of the anonymous function is just our callback, it could be a reference to a function if you'd rather.

A really clean way to do this is to use async.js reduce method. Async.js gives many nice ways to deal with multiple callbacks. You could use reduce to iterate through the array of file names, and build a reduced value which is an array of the valid lines:

<input type="file" id="files" name="files[]" multiple />
<script type='text/javascript' src='https://github./caolan/async/raw/master/lib/async.js'/>
<script>
var isValidLine = function(text){
// todo: implement
}

function handleFileSelect(evt) {
    var files = evt.target.files; // FileList object
    // reduce by starting with empty array in second argument - 
    // this gets built up with the valid array lines
    async.reduce(files, [], function(validLinesSoFar, file, callback){
        var r = new FileReader();
        // Read file here:
        r.onload = function (f) {

            var contents = f.target.result;
            var lines = contents.split('\n');

            for(var i=0; i<lines.length; i++){
                if isValidLine(lines[i])
                    validLinesSoFar.push(lines[i]);    
            }
            callback(null, validLinesSoFar); 
        };

        r.readAsText(file);
    }, function(err, validLines){
        // gets called after every file iterated through
        // result is entire valid array
        // do something here with valid array
    });
}

  document.getElementById('files').addEventListener('change', handleFileSelect, false);
</script>

I would take a look at jQuery's deferred object

Also a very relevant question that might be applicable to you.

How to fire a callback function after a for-loop is done in Jquery?

发布评论

评论列表(0)

  1. 暂无评论