0

I'm creating a program that helps to quickly set up projects. It queries the user for a name and the dependencies needed, then creates a folder and should install the dependencies in it. Dependencies are stored in an array that can be accessed without issue. However, within the loop in the child_process.exec function, the array elements come back as undefined. Here's the code:

const inquirer = require('inquirer');
const fse = require('fs-extra');
const cp = require("child_process");
const path = require('path');

(function(){
    let projectName;
    let dependencies;
    console.log("Welcome to Node Project Creator.");
    inquirer.prompt([{type: String, name: "ProjectName", message: "What would you like to name your project?"}]).then(function(answer){
        projectName = answer.ProjectName;
        console.log(projectName + " is a great name!");
        fse.mkdir(path.join(__dirname, projectName));
        console.log("Project folder created.");
        inquirer.prompt([{type: String, name: "dependencies", message: "Enter dependencies seperated by spaces."}]).then(function(answer){
            if (answer.dependencies){
                dependencies = answer.dependencies.split(" ");
                console.log("Okay, I will install your dependencies.");
                console.log(dependencies);
            } else {
                console.log("I guess you don't need any dependencies.");
            }
            try {
                process.chdir(path.join(__dirname, projectName));
                for (var i = 0; i < dependencies.length; i++){
                    cp.exec("npm install " + dependencies[i], function(err){
                        if (err){
                            console.error(err);
                        } else {
                           console.log(dependencies[i] + " installed.");
                        }
                    });
                    console.log("Thank you for using Node Project Creator.");
                }
            } catch (err){
                console.error(err);
            }
        });
    });
})();


Joe Santy
  • 31
  • 2
  • 3
  • Are you saying that dependencies[i] is undefined in the cp.exec() call? – jarmod Feb 08 '19 at 00:35
  • Yes. I can console.log dependencies[0] after process.chdir if I put in a dependency and it works fine, so I'm not sure why it returns as undefined within cp.exec. – Joe Santy Feb 08 '19 at 00:39
  • 1
    Based on what evidence? The console.log() that happens in the cp.exec() callback? That is executing *after* the for loop has completed and so the value of i is not what you'd expect it to be. – jarmod Feb 08 '19 at 00:44
  • No, that was executing and logging undefined. I had put an additional console.log before cp.exec to test and it had worked. The issue was using var rather than let. – Joe Santy Feb 08 '19 at 00:59
  • Possible duplicate of [Calling an asynchronous function within a for loop in JavaScript](https://stackoverflow.com/questions/13343340/calling-an-asynchronous-function-within-a-for-loop-in-javascript) – Heretic Monkey Feb 08 '19 at 01:01
  • Yes, it was executing and logging undefined. And that's because dependencies[i] was undefined at that point. And that's because i had the value dependencies.length at that point (it did not have the value 0) and hence you referenced an array element beyond the end of the array (and you got undefined). JavaScript's async behavior will continue to bite you until you learn how it works, and how var/let differ. – jarmod Feb 08 '19 at 01:12

1 Answers1

1

Your cp.exec() callbacks are called asynchronously, when i is already not < dependencies.length. Try let i = 0 to make i bound to each loop value.

See http://exploringjs.com/es6/ch_variables.html#sec_let-const-loop-heads

vsemozhebuty
  • 12,992
  • 1
  • 26
  • 26
  • 1
    I'm not sure why let worked where var didn't, but that fixed it. Thanks. – Joe Santy Feb 08 '19 at 00:57
  • I've added a link with some info) – vsemozhebuty Feb 08 '19 at 00:58
  • `let` in `for` loop works as in closure: each iteration value is saved in the same scope as each created callback. While the `var` variable is created in the scope outside of the loop block and represents the last value when the loop stops and callbacks are called. – vsemozhebuty Feb 08 '19 at 01:05