1

I try to parse all the files that were uploaded as a directory upload into a single array of files. I have a problem that I cannot combine result of all the promises into a single array. I get a multidimensional array instead.

function iterateThroughUploadedFiles(files, isEntry = false) {
    var promises = [];
    for (let i = 0; i < files.length; i++) {
        let entry = isEntry ? files[i].webkitGetAsEntry() : files[i];
        if (entry.isFile) {
            promises.push(new Promise(function(resolve, reject) {
                getFile(entry, resolve, reject);
            }));
        }
        else if (entry.isDirectory) {
            let dirReader = entry.createReader();

            var promise = new Promise(function(resolve, reject) {
                readEntries(dirReader, resolve, reject);
            });

            promises = promises.concat(promise);
        }
    }
    return Promise.all(promises);
}

function getFile(fileEntry, resolve, reject) {
    fileEntry.file(function(file) {
        resolve(file)
    });
}

function readEntries(dirReader, resolve, reject) {
    dirReader.readEntries(function (entries) {
        resolve(iterateThroughEntries(entries).then(function(result) {
            return result;
        }));
    });
}

Usage:

iterateThroughUploadedFiles(files, true).then(function(result) {
    console.log(result);
});

When I execute iterateThroughUploadedFiles function the result variable is a multidimensional array. However, I want this array to be flattened (example: [File(6148), Array(1), Array(0), File(14)]). I am not very familiar with callbacks and Promises...therefore, I have some issues working with them.

Edit:
There is a typo I made inside readEntries(dirReader, resolve, reject) function. There should be iterateThroughUploadedFiles instead of iterateThroughEntries function.

1
  • 2
    readEntries and getFile should return a promise that they construct themselves, not take resolve/reject as callbacks. Commented Jul 1, 2018 at 13:44

1 Answer 1

1
return Promise.all(promises);

That resolves to a two dimensional array, which you can easily flatten by using some generator functions:

function* flatten(arr) {
  for(const el of arr) {
    if(Array.isArray(el)) {
      yield* flatten(el);
    } else {
      yield el;
    }
  }
}

return Promise.all(promises).then(array => ([...flatten(array)]));

While passing resolve and reject works kind of, that can be done way more elegant with just returning a new Promise from the getFile function:

function getFile(fileEntry, resolve, reject) {
   return new Promise(resolve => fileEntry.file(resolve));
}

Then it can be easily chained

function readEntries(dirReader) {
  return new Promise(resolve => dirReader.readEntries(resolve))
    .then(iterateThroughEntries);
}

function iterateThroughUploadedFiles(files, isEntry = false) {
   return Promise.all( files.map( file => {
     let entry = isEntry ? file.webkitGetAsEntry() : file;
     if (entry.isFile) {
       return getFile(entry);
     } else if (entry.isDirectory) {
       return readEntries( entry.createReader());
     }
   })).then(array => ([...flatten(array)]));
}
Sign up to request clarification or add additional context in comments.

4 Comments

I would've thought array => [].concat(...array) would be how to flatten a two dimensional array - or even [].concat.apply([], array) if the JS engine in question supports arrow functions but not spread operator
This solves only two dimensional problem but is it possible that I have more dimensional arrays.
@david, yes it is, have a look at the edited version ;)
It's possible to use Array.flat instead of flatten

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.