shmallen
shmallen

Reputation: 13

writeFile does not wait for variable to be instantiated

I'm new to node.js and javascript in general but I am having issues understanding why the writeFile function is writing a blank file. I think the for loop should be a Promise but I am not sure how to write it.

const removeProviders = function () {
    readline.question('Where is the file located?  ', function(filePath) {
        let providerArray = fs.readFileSync(filePath).toString().split('\r\n');
        console.log(providerArray);
        let importFile = '';

        for (let providerId in providerArray) {
            getProvider(providerArray[providerId]).then((response) => {
                let providerInfo = response.data;
                return providerInfo;
            }).then((providerInfo) => {
                let entry = createImportEntry(providerInfo);
                importFile += "entry";
            })
        }

        fs.writeFile('C:/path/to/file.txt', importFile);

Upvotes: 1

Views: 339

Answers (3)

Lavneesh Chandna
Lavneesh Chandna

Reputation: 141

Your for loop does not wait for the promises to resolve. A better way to approach this problem would be to use reduce.

providerArray.reduce(
    (p, _, i) => {
        p.then(_ => new Promise(resolve => 
            getProvider(providerArray[providerId]).then((response) => {
                let providerInfo = response.data;
                return providerInfo;
            }).then((providerInfo) => {
                let entry = createImportEntry(providerInfo);
                importFile += entry;
                resolve();
            }))
        );
    }
, Promise.resolve() );

You can also use Promise.all but the out data may not be in the order that you expect if you append to the importFile variable.

Upvotes: 0

Chirag
Chirag

Reputation: 313

The below approach may be useful. I don't know your getProvider return Promise. you can set promise in getProvider method

const getProviderValue = async function(providerArray) {
    return new Promise((resolve, reject) {
        let importFile = '';
        for (let providerId in providerArray) {
            await getProvider(providerArray[providerId]).then((response) => {
                let providerInfo = response.data;
                return providerInfo;
            }).then((providerInfo) => {
                let entry = createImportEntry(providerInfo);
                importFile += "entry";
            })
        }
        resolve(importFile)
    })
}

const removeProviders = async function () {
    readline.question('Where is the file located?  ', function(filePath) {
        let providerArray = fs.readFileSync(filePath).toString().split('\r\n');
        console.log(providerArray);
        let importFile = await getProviderValue(providerArray)
        fs.writeFile('C:/path/to/file.txt', importFile);
    })
}

Upvotes: 1

pintxo
pintxo

Reputation: 2155

You can collect all the promises from the for-loop into an Array and then pass them into Promise.all() which will resolve only after all of them resolved. If one of the promises are rejected, it will reject as well:

const promises = providerArray.map((item) => {

  return getProvider(item)
    .then((response) => {
      let providerInfo = response.data;
      return providerInfo;
    })
    .then((providerInfo) => {
      let entry = createImportEntry(providerInfo);
      importFile += "entry";
    });

});

Promise.all(promises).then(() => {

  fs.writeFile('C:/path/to/file.txt', importFile, err => {

    if (err) {
      console.error(err);
    }

  });

});

While doing this you could also get rid of the importFile variable and collect directly the results of your promises. Promise.all().then(results => {}) will then give you an array of all results. Thus no need for an updatable variable.

Upvotes: 2

Related Questions