0

So I have this asynchronous function with inquirer and fs.writeFile inside

(async () => {

...

  if (process.env.IG_USERNAME && process.env.IG_PASSWORD) {
    console.log(`Used as ${chalk.green(process.env.IG_USERNAME)}`);
  } else {
    console.log(`\nInstagram account data is not yet put in \nInputting in...`);

    await inquirer.prompt(questions).then((answers) => {
      let file = `IG_USERNAME=${answers.username}\nIG_PASSWORD=${answers.password}\n#ONLINE_MODE=true`;
      fs.writeFile(".env", file, (err) => {
        if (err) console.log("Something went wrong..");
        else console.log(`Used as ${chalk.green(process.env.IG_USERNAME)}`);
      });
    });
  }

  await login();

...

})();

the login(); function needs the .env variable, i input it using inquirer but the login(); function get executed before the inquirer answer get processed.

What should I do to make the login(); waits until the fs.writeFile is finished?

Syakhisk Al-azmi
  • 80
  • 1
  • 4
  • 9

2 Answers2

1

The short answer is to ONLY use promise-based asynchronous operations inside your .then() so you can properly chain all promises and then the higher level await will wait for everything. In this case, you can use return fs.promises.writeFile(...) (instead of fs.writeFile(...)) because fs.promises.writefile(...) returns a promise and will then properly chain with the parent promise.

(async () => {

...

  if (process.env.IG_USERNAME && process.env.IG_PASSWORD) {
    console.log(`Used as ${chalk.green(process.env.IG_USERNAME)}`);
  } else {
    console.log(`\nInstagram account data is not yet put in \nInputting in...`);

    await inquirer.prompt(questions).then((answers) => {
      let file = `IG_USERNAME=${answers.username}\nIG_PASSWORD=${answers.password}\n#ONLINE_MODE=true`;
      return fs.promises.writeFile(".env", file);  
    });
  }

  await login();

...

})();

Or, simplified a little more:

(async () => {

...

  if (process.env.IG_USERNAME && process.env.IG_PASSWORD) {
    console.log(`Used as ${chalk.green(process.env.IG_USERNAME)}`);
  } else {
    console.log(`\nInstagram account data is not yet put in \nInputting in...`);

    const answers = await inquirer.prompt(questions);
    const file = `IG_USERNAME=${answers.username}\nIG_PASSWORD=${answers.password}\n#ONLINE_MODE=true`;
    await fs.promises.writeFile(".env", file);  
  }

  await login();

...

})();
jfriend00
  • 683,504
  • 96
  • 985
  • 979
  • i tried both example but the next statement/function seems to be executed first, even when i do `console.log(process.env.IG_USERNAME)` it still gives me `undefined` – Syakhisk Al-azmi Aug 16 '20 at 07:45
  • i think i found the problem. it is not about the promises, it is about the .env file that i require at the top of the program so it wouldn't be change until i required it again in the next run of the program. – Syakhisk Al-azmi Aug 16 '20 at 08:13
0

Generally either use .then() or await with promises, not both together. And login() is getting executed before because the promise is getting resolved via .then().

And there is no promise returned to await for before calling login().

Solution 1: The quick fix is to resolve await login(); inside the callback of fs.writeFile, but then login()'s error would have to be handled as well, which just makes the code more verbose to begin with.

const answers = await inquirer.prompt(questions);
let file = `IG_USERNAME=${answers.username}\nIG_PASSWORD=${answers.password}\n#ONLINE_MODE=true`;
fs.writeFile(".env", file, async (err) => {
  if (err) console.error("Error:fs.writeFile()::", err);
  else console.log(`Used as ${chalk.green(process.env.IG_USERNAME)}`);
  try {
    await login();
  } catch (e) {
    console.error("Error::login():", e);
  }
});

Solution 2: util.promisify with Node >= V8

Make the fs.writeFile to a promise.

const fs = require("fs");
const { promisify } = require("util");
const promisedWriteFile = promisify(fs.writeFile);

try {
  const answers = await inquirer.prompt(questions);
  let file = `IG_USERNAME=${answers.username}\nIG_PASSWORD=${answers.password}\n#ONLINE_MODE=true`;
  await promisedWriteFile(".env", file);
  await login();
} catch (error) {
  console.error("Any error::", error);
}

Solution 3: fs Promises API with Node >= V10

try {
  const answers = await inquirer.prompt(questions);
  let file = `IG_USERNAME=${answers.username}\nIG_PASSWORD=${answers.password}\n#ONLINE_MODE=true`;
  await fs.promises.writeFile(".env", file);
  await login();
} catch (error) {
  console.error("Any error::", error);
}
ambianBeing
  • 3,449
  • 2
  • 14
  • 25
  • i tried it but the data from the `.env` is still undefined even after the `writeFile()` – Syakhisk Al-azmi Aug 16 '20 at 07:46
  • @SyakhiskAl-azmi This should work! check the `.env` file if it has the values of `let file` string? It's probably how you are reading values from `.env` file. Please update that as well in the question to have more context. – ambianBeing Aug 16 '20 at 08:11
  • yes it should've worked. the data is inputted to the .env but i think my problem is that i loaded the `.env` is constant and i still couldn't find a way to reload it. thanks! – Syakhisk Al-azmi Aug 16 '20 at 15:32
  • I assume you are using `dontenv` for environment variables, and you suspected right it will not reload the `.env` file and assign variables (if `.env` is changed in the middle of some code). A way to do this: [overriding the env variables listed here](https://github.com/motdotla/dotenv#what-happens-to-environment-variables-that-were-already-set). Also please do consider accepting one of the answers as they solve the promise problem mentioned in your question. Good luck! – ambianBeing Aug 17 '20 at 05:02