Adarsh Hegde
Adarsh Hegde

Reputation: 631

unable to set proper scope for variable within a JavaScript promise

I've come across a weird issue where a new variable is being created in local scope even if it is defined outside,

from the below code

after I call buildMeta() and check the contents of "data", it is always empty implying that it's not being modified at all, even if I've specifically targeted "that.data" where that refers to the class' object.

I'd appreciate if anyone would point out what I am doing wrong.

class meta {

    constructor(files) {
        if(!files) throw Error("files not specified");
        this.data = {};
        this.ls = files;
    }

  buildMeta() {
        var that = this;
        for(let i = 0; i < that.ls.length; i++) {

            mm.parseFile(that.ls[i]).then(x => {
                var info = x.common;
                that.data[info.artist] = "test";
            }).catch((x) => { 
               console.log(x);    
            });
       }

    }
 }
const mm = new meta(indexer); // indexer is an array of file paths
mm.buildMeta();
console.log(mm.data);

Upvotes: 2

Views: 60

Answers (2)

Pehota
Pehota

Reputation: 116

You're mixing sync with async code here. The for loop won't wait for the parseFile promises to resolve. You could use Promise.all to fill in the data when the files are parsed.

// Class names should be written using a capital letter 
class Meta {
    ...
    buildMeta() {
     // You don't need this assignment since you're using arrow functions
    // var that = this;
    const promises = this.ls.map(filePath => mm.parseFile(filePath));
    return Promise.all(promises).then(resolvedPromises => {
        resolvedPromises.map(({ parsedFile }) => {
            this.data[parsedFile.common.artist] = "test";
       });
       return this.data;
    }).catch(console.error);
}

...

const mm = new meta(indexer); // indexer is an array of file paths
mm.buildMeta().then(data => {console.log(data)});

Hope this helps.

Upvotes: 2

Martin Stone
Martin Stone

Reputation: 13007

You are logging mm.data before parseFile has finished. Your code implies that it returns a promise, so your insertion into that.data will happen after your console.log(mm.data) executes.

You need to return a promise from buildMeta, so that you can do...

const mm = new meta(indexer);
mm.buildMeta().then(() => {
    console.log(mm.data);
})

Here's a buildMeta that should do what you need. This returns a promise that waits for all of the parseFile invocations to do their work and update this.data...

buildMeta() {
    return Promise.all(this.ls.map(f => mm.parseFile(f).then(x => {
        var info = x.common;
        this.data[info.artist] = "test";
    })))
}

Upvotes: 1

Related Questions