0

I'm trying to read files inside several folders and I need the whole code goes synchronously.

I converted Callback into Promise and try to await it:

function getEntries(reader) {
    return new Promise((resolve, reject) => {
        reader.readEntries((entries) => resolve(entries))
    })
}

async function onDrop(e) {
    e.preventDefault();
    var items = e.dataTransfer.items;
    for (var i=0; i<items.length; i++) {
        
        var entry = items[i].webkitGetAsEntry();
        if (entry.isDirectory) {
            var reader = entry.createReader();
            var entries = await getEntries(reader);
            console.log(entries);
        }
    }
}

But for loop goes just for the first folder. What's wrong? How I should reorganize my code?

Mikhail
  • 101
  • 2
  • 8
  • change `getEntries ` to an async function. and await for reader as well – sojin Dec 14 '21 at 13:52
  • 1
    `.webkitGetAsEntry()` is non-standard: better to use [`DataTransferItem.getAsFileSystemHandle()`](https://developer.mozilla.org/en-US/docs/Web/API/DataTransferItem/getAsFileSystemHandle) – jsejcksn Dec 14 '21 at 13:59
  • off topic: `const getEntries = reader => new Promise(reader.readEntries.bind(reader));` – Thomas Dec 14 '21 at 15:31

2 Answers2

2

UPDATE

The reason the await did not seem to work is due to how browsers handle DataTransfers in the context of async code. See this SO post for details:

Here is a jsfiddle that shows the problem clearly:

My original answer below is one way to deal handle if you actually wanted an async flow.

ORIGINAL

I think you meant to say that you want to read directories asynchronously? If so...

As it is, the loop will await on each directory. Instead, perhaps you want to collect the promises and continue only when all of them have resolved. Something like:

function getEntries(reader) {
    return new Promise((resolve, reject) => {
        reader.readEntries((entries) => resolve(entries))
    })
}

async function onDrop(e) {
    e.preventDefault();
    var items = e.dataTransfer.items;
    var promises = [];
    for (var i=0; i<items.length; i++) {
        
        var entry = items[i].webkitGetAsEntry();
        if (entry.isDirectory) {
            var reader = entry.createReader();
            promises.push(getEntries(reader));
        }
    }
    Promise.all(promises).then((entries) => {
     console.log(entries);
    });
}
kaliatech
  • 17,579
  • 5
  • 72
  • 84
  • Thanks. It works! So isn't possible to use `var entries = await getEntries(reader);` inside loop? – Mikhail Dec 14 '21 at 14:05
  • It depends on what you want to do. Using `await` inside the loop will make the loop synchronous. It will stop at the `await` until the promise resolves. – kaliatech Dec 14 '21 at 14:08
  • But `await` inside loop doesn't work. It works for the first folder, but then there is no more `console.log`. Looks like the second and next promises don't resolve – Mikhail Dec 14 '21 at 14:13
  • Ah, now I understand the confusion. The reason is because of how file DataTransfers are handled in the browser. It's kind-of special case, and is explained in detail here: https://stackoverflow.com/questions/55658851/javascript-datatransfer-items-not-persisting-through-async-calls. You would have to change your code to either copy the data transfer items BEFORE using async/await calls, or you would to have use the promise collection approach per my example. – kaliatech Dec 14 '21 at 15:03
  • Thank you very much. Now I got it – Mikhail Dec 15 '21 at 03:56
0

Try using for-of loop instead of for loop only if you want the previous iteration to complete before moving to next iteration. If that is not important just push every call to getEntries in a promise and await for that promise at end.

Smriti Shikha
  • 421
  • 2
  • 6