Asynchronous drag and drop multiple files / directories not awaiting properly

Viewed 277

While the code is correct and eventually gets all the file paths in all subdirectories the files array is empty at the end of handleDrop and doesn't properly wait for walkDirRecursively.

I've tried adding Promise and resolving etc. but it does not await properly.

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">

    <style>
        #dropzone {
            width: 100vw;
            height: 100vh;
        }
    </style>
    <title>Document</title>
</head>
<body>
    <div id="dropzone" ondragover="handleDragOver(event)" ondrop="handleDrop(event)"></div>
    
    <script>
        let files = [];

        function handleDragOver(event) {
            event.preventDefault();
        }

        async function handleDrop(event) {
            event.preventDefault();
            files = [];
            
            const dataTransferItems = Object.keys(event.dataTransfer.items);

            await dataTransferItems.map(async (key) => {
                const item = event.dataTransfer.items[key];
                const entry = item.webkitGetAsEntry();
                
                if (!entry) return;

                if (entry.isFile) {
                    files.push(entry.fullPath);
                } else if (entry.isDirectory) {
                    const directoryReader = entry.createReader();
                    await walkDirRecursively(entry.fullPath, directoryReader);
                }
            });

            console.log(files);

        }

        function walkDirRecursively(path, directoryReader) {
            return new Promise(async (resolve, reject) => {
                directoryReader.readEntries(async (entries) => {
                    await entries.map(async (entry) => {
                        if (entry.isFile) {
                            const newPath = path + "/" + entry.name;
                            files.push(newPath);
                        } else if (entry.isDirectory) {
                            const newPath = path + "/" + entry.name;
                            const newDirectoryReader = entry.createReader();
                            await walkDirRecursively(newPath, newDirectoryReader);
                        }
                    });
                });
                resolve();
            });
        }
    </script>
</body>
</html>

Trying out the example should speak for itself after dropping a folder on the page.

1 Answers

Adding Promise.all to both loops and wrapping the walkDirRecursively in a Promise and only resolving it when the inner loop is done is what was needed.

An alphabetic sort was also added since it seems to process the files / folders ad hoc and not in the order the OS stores them.

    async function handleDrop(event) {
        event.preventDefault();
        files = [];
        
        const dataTransferItems = Object.keys(event.dataTransfer.items);

        console.log(dataTransferItems);

        await Promise.all(dataTransferItems.map(async (key) => {
            const item = event.dataTransfer.items[key];
            const entry = item.webkitGetAsEntry();
            
            if (!entry) return;

            if (entry.isFile) {
                files.push(entry.fullPath);
            } else if (entry.isDirectory) {
                const directoryReader = entry.createReader();
                return await walkDirRecursively(entry.fullPath, directoryReader);
            }
        }));

        console.log(files);
    }


    function walkDirRecursively(path, directoryReader) {
        return new Promise(async (resolve, reject) => {
            directoryReader.readEntries(async (entries) => {
                await Promise.all(sortListAlphabetically(entries).map(async (entry) => {
                    if (entry.isFile) {
                        const newPath = path + "/" + entry.name;
                        files.push(newPath);
                    } else if (entry.isDirectory) {
                        const newPath = path + "/" + entry.name;
                        const newDirectoryReader = entry.createReader();
                        await walkDirRecursively(newPath, newDirectoryReader);
                    }
                }));
                resolve();
            });
        });
    }

    function sortListAlphabetically(list) {
        return list.sort((a, b) => a.name.localeCompare(b.name));
    }
Related