-1

Tried to get my head around this problem, but still couldn't(I feel like ELI5 would be appropriate approach for me when talking about Promises). I removed readMultipleFiles-function promise. Put Promise.all and tried async await, but the returned array is still empty. I'm completely lost and I would appreciate very much if you could point me my error(s) and maybe suggest some good YT lessons about these :/

Edited code and added all the required JS and HTML+CSS to create dropzone:

// Function to handle files user drag&drop to HTML page
const handleDrop = async (e) => {
console.log('1.0 handleDrop function starts... ');

    // files are filelist-type
    const dt = e.dataTransfer;
    const files = dt.files;
    let fileArray = [...files];

    console.log('2. Starting to read files.');
    let filesRead = await readMultipleFiles(fileArray);

    // These gives still undefined
    console.log('filesRead[1]: ',filesRead[1]);
    console.log('filesRead: ',filesRead);
}

// Function to read multiple files in array. This should return array of files
const readMultipleFiles = async (fileArray) => {
    console.log('\t2.1 readMultipleFiles working...');
    return Promise.all(fileArray.map(f => {readFile(f)}));
}

// Function to read single file. This should return single file object:
// {filename: <somefilename>, content:<sometextcontent}
const readFile = (file) => {
    console.log('\t2.2 readFile working...');
    let myPromise = new Promise((resolve) => {
        let singleFile;
        // console.log('\t\t2.2.1 Reading single file...');
        const reader = new FileReader();
        reader.onload = (e) => {
            // console.log('\t\t\t2.2.1 readFile -> file: ',e.target.result, 'typeof file:', typeof e.target.result);
            fileContent = e.target.result;
            fileName = file.name;
            singleFile = {
                'filename':file.name,
                'content':e.target.result
            };
            // console.log('\t2.2.1 readFile --> fileRead: ', singleFile);
            
        }
        reader.readAsText(file);
        resolve(singleFile);
    })
    return myPromise.then(res => {
        // console.log('2.2.1.1 readfile -> myPromise.then res: ', res);
        return res;
    });
}

const initApp = () => {
    const droparea = document.querySelector('.droparea');

    const  active = () => droparea.classList.add('green-border');
    const inactive = () => droparea.classList.remove('green-border');

    const prevents = (e) => e.preventDefault();

    ['dragenter', 'dragover', 'dragleave', 'drop'].forEach(evtName => {
        droparea.addEventListener(evtName, prevents);
});

    ['dragenter', 'dragover'].forEach(evtName => {
    droparea.addEventListener(evtName, active);
});

    ['dragleave', 'drop'].forEach(evtName => {
    droparea.addEventListener(evtName, inactive);
});
    droparea.addEventListener('drop', handleDrop);
}

document.addEventListener("DOMContentLoaded", initApp);

HTML page for this:

<html>
<head>
    <meta charset="utf-8">
    <title>Page</title>
    <script src="https://ajax.googleapis.com/ajax/libs/jquery/3.2.1/jquery.min.js"></script>
    <link href="https://fonts.googleapis.com/css?family=Lato" rel="stylesheet">
    <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.1.2/css/all.min.css" integrity="sha512-1sCRPdkRXhBV2PBLUdRb4tMg1w2YPf37qatUFeS7zlBy7jJI8Lf4VHwWfZZfpXtYSLy85pkm9GaYVYMfw5BC1A==" crossorigin="anonymous" referrerpolicy="no-referrer" />
    <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/semantic-ui/2.2.10/semantic.min.css">

    <link rel="stylesheet" href="styles.css">
    </head>
  <body>
<div>
  Page
</div>
<main>
    <section class="droparea">
        <i class="far fa-images"></i>
        <p>Drop your files here</p>
    </section>
</main>
<script type="text/javascript" src="index.js"></script>

CSS:

.hide {
  display: none;
}
h3 {
  margin: 0 auto;
  padding-top: 10px;
}
.checkboxarea {
  width: 99%;
  overflow: hidden;
}
.checkboxitem {
  width: 45%;
  float: left;
}
.ui.segment {
  border: 0;
  box-shadow: none;
}

#drop_zone {
  border: 5px solid blue;
  width:  200px;
  height: 100px;
}

.droparea {
  margin: 1rem auto;
  display: flex;
  flex-direction: column;
  justify-content: left;
  align-items: center;
  width: 384px;
  max-width: 100%;
  height: 160px;
  border: 4px dashed grey;
  border-radius: 15px;
}

.droparea i {
  font-size: 3rem;
  flex-grow: 1;
  padding-top: 1rem;
}

.green-border {
  border-color: green;
}

.selectResultData {
  padding-right: 10px;
}

/* Style the button that is used to open and close the collapsible content */
.collapsible {
  background-color: #eee;
  color: #444;
  cursor: pointer;
  padding: 18px;
  width: 100%;
  border: none;
  text-align: left;
  outline: none;
  font-size: 15px;
}

/* Add a background color to the button if it is clicked on (add the .active class with JS), and when you move the mouse over it (hover) */
.active, .collapsible:hover {
  background-color: #ccc;
}

/* Style the collapsible content. Note: hidden by default */
.content {
  padding: 0 18px;
  display: none;
  overflow: hidden;
  background-color: #f1f1f1;
}

Original question:

I've been banging my head with this kind of problem for awhile now. I'm trying to use Javascript promises and async/await to fill array with object(txt-files dropped by user to HTML page) to be used later on my code but I cannot get around problem that array objects cannot be accessed although console.log shows array has objects. Seems to me that async/await is not waiting for the Array to be filled for some reason which I don't understand. Can you please help me with this?

//Code to handle user filedrops
const handleDrop = async (e) => {
    console.log('1.0 handleDrop function starts... ');
    
    // files are filelist-type
    const dt = e.dataTransfer;
    const files = dt.files;
    let fileArray = [...files];
    // Making sure files are in fileArray and they can be accessed
    console.log('1.0 handleDrop -> fileArray.length',fileArray.length)
    console.log('Files: ',fileArray);

    console.log('2. Starting to read files.');
    let filesRead = await readMultipleFiles(fileArray);
    console.log('This log row should come after files have been read and saved to variable\nFiles read! filesRead: ', filesRead);
    
    // **************************
    // Why this gives undefined?
    // **************************
    console.log('filesRead[1]: ',filesRead[1]);
}

//Function to read Array of files and return them back in Array
const readMultipleFiles = (fileArray) => {
    console.log('\t2.1 readMultipleFiles working...');
    let myPromise = new Promise((resolve) => {
        let filesRead = [];
        fileArray.forEach(f => {
            let file;
            readFile(f).then(res => {
                console.log('2.1.1 file returned by readFile', res, '\nf:',f);
                file=res
                return res;
            }).then(res => filesRead.push(file));
        });
        resolve(filesRead);
    })
    return myPromise.then(res => res);
}

// Function to read single file and return it as an object
const readFile = (file) => {
    console.log('\t2.2 readFile working...');
    let myPromise = new Promise((resolve) => {
        let singleFile;
        console.log('\t\t2.2.1 Reading single file...');
        const reader = new FileReader();
        reader.onload = (e) => {
            console.log('\t\t\t2.2.1 readFile -> file: ',e.target.result, 'typeof file:', typeof e.target.result);
            fileContent = e.target.result;
            fileName = file.name;
            singleFile = {
                'filename':file.name,
                'content':e.target.result
            };
            console.log('\t2.2.1 readFile --> fileRead: ', singleFile);
            resolve(singleFile);
        }
        reader.readAsText(file);       
    })
    return myPromise.then(res => {
        console.log('2.2.1.1 readfile -> myPromise.then res: ', res);
        return res;
    });
}
Damii
  • 13
  • 4
  • 1
    Don't mix `async / await` with `.then / .catch` unless you have a VERY good reason. In this case, you dont. Refractor to use async await everywhere and the problem will clear up for you. – Joel Oct 18 '22 at 10:25
  • ^^ yes, but don't use async/await in a forEach - use a for...of loop instead - or to make the reads in parallel, use .map returning a Promise, and then await `Promise.all(mappedPromises)` – Jaromanda X Oct 18 '22 at 10:33
  • Your example isn't verifiable [mcve]. Have a look here on some changes you can do: https://stackblitz.com/edit/js-huthwl?file=index.js – Joel Oct 18 '22 at 10:33
  • Hi! Thanks for looking for this.I tried to get this working in stackbliz, but I got only this: https://js-uwapya.stackblitz.io. I could not get around why my JS functions are not working there. I was trying to launch handleDrop-function after 2 txt files are dropped to dropzone. I hope this makes my problem little bit clearer. Also I'd like to know why the index.js is not working in stackblitz, please. – Damii Oct 18 '22 at 12:23
  • and ofcourse my link directed to page, not the workspace :( Editor URL is this: https://stackblitz.com/edit/js-uwapya – Damii Oct 18 '22 at 12:25

1 Answers1

-1

Your function readMultipleFiles returns a promise that resolves to an empty array because resolve is called before the asynchronous then callbacks have ran, and so filesRead is still [] when resolve(filesRead) is called.

Not your original problem, but it is an anti-pattern to create a promise with new Promise when you already have promises to work with (readFile(f)). In this case you probably want to get a promise from Promise.all

Here is how you can fix readMultipleFiles:

const readMultipleFiles = (fileArray) => {
    console.log('\t2.1 readMultipleFiles working...');
    return Promise.all(fileArray.map(readFile))
}

For simplicity sake, I didn't bother to have this line in the above correction:

console.log('2.1.1 file returned by readFile', res, '\nf:',f);

There is already a lot of debugging output there.

trincot
  • 317,000
  • 35
  • 244
  • 286
  • Thanks for looking into this! I will check this tomorrow after sleeping the night since my mind is numb and currently wondering in Promiseland on it's own ;) – Damii Oct 18 '22 at 15:48