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;
});
}