1

I'm new to asynchronous programming

function loadPlugin(plugins, callback) {
    let tests = [[], [], []];
    plugins.forEach((plugin) => {
        f.isPlugin(plugin, (exists) => {
            if (exists) {
                if (!f.pluginIsLoaded(plugin)) {
                    tests[0].push(plugin);
                    f.loadPlugin(plugin);
                } else {
                    tests[1].push(plugin);
                }
            } else {
                tests[2].push(plugin);
            }
        });     
        console.log(tests);       
    });
    return tests;
}

and

module.exports.isPlugin = (plugin , callback) => {
    fs.access(`./plugins/${plugin}`, fs.constants.F_OK, (err) => {
        callback(!err);
    });
};

Inside f.isPlugin(plugin, (exists) => { }); I push plugin into the tests array and I console.log(tests) from the outer function it shows that the tests array is an array containing 3 empty arrays in it.

Is there a way I can retain the the stuff pushed inside f.isPlugin(plugin, (exists) => { }); so I can access it from the outer functions?

5
  • 1
    use callback(from an outer function) to check that array since callback should be the last operation in an async function. async functions don't simply return, they callback. Commented Nov 27, 2016 at 19:12
  • No, you cannot. Async results must be used inside the async callback or you can call some other function from within the inner scope and pass the data to it. You cannot use it synchronously in the outer scope. You could use a callback in the outer scope that would get called from the inner scope. And, you'll have to wait for all the async operations to be done too. Commented Nov 27, 2016 at 19:13
  • 2
    You need to be sure the operations on the array actually run to completion, it's not that the stuff you pushed in the array is getting lost, it just hasn't necessarily happened yet. Commented Nov 27, 2016 at 19:13
  • I'm calling f.isPlugin which is inside plugins.forEach. I want it to let it loop all and fill the tests before returning it, I'm not sure how to approach it. Commented Nov 27, 2016 at 19:16
  • looping in many callbacked functions should be fit for async.waterfall maybe? "Runs an array of functions in series, each passing their results to the next in the array" Commented Nov 27, 2016 at 19:20

3 Answers 3

2

You could switch from the callback pattern to promises and use Promise.all to wait for all plugin checks to complete:

module.exports.isPlugin = plugin => new Promise( resolve =>
    fs.access(`./plugins/${plugin}`, fs.constants.F_OK, err => resolve(!err))
);

function loadPlugin(plugins) {
    let tests = [[], [], []];
    let promises = plugins.map( plugin => 
        f.isPlugin(plugin).then( exists => {
            let status = !exists ? 2 : +f.pluginIsLoaded(plugin);
            if (!status) f.loadPlugin(plugin);
            tests[status].push(plugin);
        })
    );
    // Turn the array of promises into one promise, 
    // which provides the tests array when it resolves:
    return Promise.all(promises).then( _ => tests );
}

So you would call this as:

loadPlugin(plugins).then( tests => console.log(tests) );
Sign up to request clarification or add additional context in comments.

Comments

1

You can use Promises and async functions.

Firstly, change the isPlugin function to return a Promise:

module.exports.isPlugin = plugin => new Promise(resolve =>
  fs.access(`./plugins/${plugin}`, fs.constants.F_OK, err => resolve(!err))
);

Then change the loadPlugin function to an async function:

async function loadPlugin(plugins) {
  let tests = [[], [], []];
  for (const plugin of plugins) {
    const exists = await f.isPlugin(plugin);
    if (exists) {
      if (!f.pluginIsLoaded(plugin)) {
        tests[0].push(plugin);
        f.loadPlugin(plugin);
      } else {
        tests[1].push(plugin);
      }
    } else {
      tests[2].push(plugin);
    }
  }
  return tests;
}

Note that async functions aren't yet supported in Node.js, so you'll have to use Babel to transpile your code.

Comments

1

You can't return something from a function with a callback you must call the callback function with the result. This is not an issue of accessing the array, the array is being printed before anything has had a chance to be pushed. If you like returning (which you should it makes a lot of sense) I would recommend using promises. Read up on promises here. I just made an answer using callbacks but I realized that wouldn't work every time. To be honest I don't know how to do it with callbacks. Here is a solution with Promises.

function loadPlugin(plugins) {
    let tests = [[], [], []];
    // This maps your array to a promise for each one that resolves when the task is complete
    let promises = plugins.map((plugin) => {
      return new Promise((resolve, reject) => {
        f.isPlugin(plugin, (exists) => {
          if (exists) {
            if (!f.pluginIsLoaded(plugin)) {
              tests[0].push(plugin);
              f.loadPlugin(plugin);
            } else {
              tests[1].push(plugin);
            }
          } else {
            tests[2].push(plugin);
          }
          // Tells the promise the task is complete
          resolve();
        }); 
      });                    
    });
    // Wait for all of the tasks to complete then return tests
    return Promise.all(promises).then(() => tests);
}

@Gothdo's solution is better but I wasn't sure if you were using a transpiler to use the async await syntax. I would recommend doing so and going with his/her solution.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.