1

I've been learning about Promises in JS and although its been pretty exciting its also been a bit frustrating. So I'm working on some code that will allow users to drag-and-drop Files and Folders to my web app. However some processes are dependent on others to complete.

My idea is to capture all fileItems (for additional context they're of type FileSystemEntry), then convert them to an object type 'File Upload Object' (my own custom class), then push them all into an array of the same type, to then finally display them on the screen.

Here is my attempt to the solution:

First I want to create a new instance of an object so I can push it into the array.

async returnFileUploadObject(file): Promise<FileUploadObject> {
    let fileModTime;
    let fileSize;
    return new Promise((resolve, reject) => {
        try {
            file.getMetadata((metadata) => {
                fileModTime = metadata.modificationTime;
                fileSize = metadata.size;
                const fileUploadObj = new FileUploadObject(file.name, fileModTime, fileSize, String(this.uploadState.Spinner), false, file.fullPath);
                resolve (fileUploadObj);
            });
        } catch (e) {
            fileModTime = Date.now();
            fileSize = 0;
            const fileUploadObj = new FileUploadObject(file.name, fileModTime, fileSize, String(this.uploadState.Spinner), false, file.fullPath);
            resolve(fileUploadObj);
        }
    });
}

Then, I want to push that new object into an array.

 async createFileObjectList(fileEntries): Promise<FileUploadObject[]> {

        return new Promise(async (resolve, reject) => {
            let list = [];
            for (let file of fileEntries) {

                let fileObj = await this.returnFileUploadObject(file);
                list.push(fileObj);

            }
            console.log('Time to resolve this promise', list);
            resolve(list);
        });
    }

Once the array is finished being built, I want to pass it back to another list that will then display the files in HTML:

async dropFiles(items) {
          // createFileObjectList
          this.fileUploadList = await this.createFileObjectList(items);
          console.log('File Upload List', this.fileUploadList);
    }

I thought I was doing everything correctly with Promises but when I console.log the results, the arrays appear to have items (28 FileUploadObjs) but actually have a length of 0. I also want to note that sometimes the console.log statement "console.log('Time to resolve this promise', list);" does print out the items in the array - sometimes.

It "looks" like the array has items but it doesnt.

Any kind of help would be greatly appreciated! I am really trying my best to understand Promises and I want my code to finally work. Also any tips for better coding practices when using Promises would be awesome too, thanks! If you need more clarification, I'd be happy to provide it to you.

  • 1
    The console output means that it works correctly up to that point but then somewhere else the list is emptied again. – Guy Incognito Jul 15 '20 at 16:30
  • @GuyIcognito How so? The code I posted above is all that I use and do so sequentially. Does it have to do with Promises? – Michael Valentine Jul 15 '20 at 16:36
  • 2
    The first line (that starts with "(28)") shows the array as it is when you log it. When you expand the array in the console (the line with "length: 0") it shows the array as it is at that point, when you expand it in the console. See https://stackoverflow.com/questions/4057440/is-chromes-javascript-console-lazy-about-evaluating-arrays – Guy Incognito Jul 15 '20 at 16:38
  • 2
    You absolutely don't want an `async` argument to the promise constructor. Not sure if this is related to your problem, but it's definitely also bad. Avoid `new Promise()` when possible. – Evert Jul 15 '20 at 16:54
  • @GuyIncognito so I think you're right about that, I've been able to iterate over the array that was returned by the Promise. Thanks for pointing that out. Still facing a little bit of trouble, but I don't think it's related to this Promise stuff. I'll clarify later once I figure out whats going on. – Michael Valentine Jul 15 '20 at 17:01
  • @Evert shoot why is that bad? What I should I use instead of `new Promise()`? – Michael Valentine Jul 15 '20 at 17:02
  • Googling "Promise constructor anti-pattern' should give you some good results. Most of these can be rewritten more cleanly without `new Promise()` – Evert Jul 15 '20 at 17:03
  • @Evert Awesome thanks. It seems this Promise stuff truly is a rabbit hole of information. Pretty cool, I'll try to see if I can apply some changes once I get this code really working. – Michael Valentine Jul 15 '20 at 17:05
  • 1
    @MichaelValentine See also [Never pass an `async function` as the executor to `new Promise`](https://stackoverflow.com/q/43036229/1048572). – Bergi Jul 15 '20 at 17:06
  • 1
    Hey everyone, thanks so much my code is finally working! So my async functions were always working as expected (although they're not following correct coding practices) but I was mislead by console.log statements showcasing incorrect info. Unfortunately I took it as gospel and it never occurred to me to test it differently - rookie mistake. I will post an answer with better coding practices once I test that the changes work. Thanks again everybody! – Michael Valentine Jul 15 '20 at 17:53

0 Answers0