0

Javascript/Node beginner here.

I've consulted several posts (Link 1, Link 2, Link 3) but I am getting an error on my following Node.js code:

const execFile = require('child_process').execFile;

async function execute(){
  let result = await sh();
  console.log("RESULT: " + result);
}

async function sh(){
  execFile('./demo-files/demo.sh', ['1st', '2nd', '3rd'], 
  function(err, data){
    let returnValue;
    if(err){
      returnValue = "ERROR:\n" + err;
    }
    else {
      returnValue = data.toString();
    }
    console.log("return value in fxn: " + returnValue);
    return returnValue;
  });
}

execute();

The console output:

RESULT: undefined
return value in fxn: 2nd

Why does my await not wait for the result of sh()? I expected it to return the value "2nd".

(The bash script is inconsequential; it's just echo $2)

Alex Coleman
  • 607
  • 1
  • 4
  • 11
  • you don't await inside `sh` ... what do you expect to wait for? you don't even return anything from that function either – Bravo Nov 29 '20 at 22:17
  • First, `await` only does something useful when you await a promise. Second, `sh()` doesn't return a promise so third, the await has no effect at all. – jfriend00 Nov 29 '20 at 22:50

2 Answers2

3

You await your function, but that function is not really async. When a function has async keyword before it, what it actually does is wrap the whole function to Promise instance. But if you won't handle what is inside this function correctly, it won't work.

In your case, you need return Promise instance manually. It accepts callback with resolve & reject arguments. And you can call resolve to resolve the promise:

function sh() {
    return new Promise(resolve => {
        execFile('./demo-files/demo.sh', ['1st', '2nd', '3rd'],
            function (err, data) {
                let returnValue;
                if (err) {
                    returnValue = "ERROR:\n" + err;
                }
                else {
                    returnValue = data.toString();
                }
                console.log("return value in fxn: " + returnValue);
                resolve(returnValue);
            })
    })
}
KiraLT
  • 2,385
  • 1
  • 24
  • 36
  • in this case you wouldn't use `async` since you have no need to use `await` inside `sh` – Bravo Nov 29 '20 at 22:17
  • Good answer. @Bravo is correct though, please remove the unused (confusing) `async` keyword. – Randy Casburn Nov 29 '20 at 22:22
  • `async` isn't used to say "hey, this function does something asynchronous" - it's used to say "I want to use `await` on a Promise inside this function to make the code simpler – Bravo Nov 29 '20 at 22:23
  • Sure, removed it, it's not necessary, especially if you don't understand what it dooes. I used it because of habit. The code style I use recommends marking all async functions (including function which returns promise) with `aync` keyword, I even have eslint rule for that in all projects. – KiraLT Nov 29 '20 at 22:26
  • @KiraLT - I'd wager you don't fully understand what it does if you use it in functions that are asynchronous, regardless if they use promises or not – Bravo Nov 29 '20 at 22:33
  • It is discussable if it's actually useful, you can find more on https://github.com/typescript-eslint/typescript-eslint/blob/master/packages/eslint-plugin/docs/rules/promise-function-async.md – KiraLT Nov 29 '20 at 22:51
0

Since execFile doesn't return a Promise, and async/await is syntax sugar for working with promises, your code can never "wait" for the execFile to run

An alternative is to use node.js "promisify"

const util = require('util');
const execFile = util.promisify(require('child_process').execFile);

then your sh function can be

const util = require('util');
const execFile = util.promisify(require('child_process').execFile);

async function execute(){
  let result = await sh();
  console.log("RESULT: " + result);
}

function sh(){
  return execFile('./demo-files/demo.sh', ['1st', '2nd', '3rd'])
  .then(data => data.toString())
  .catch(err => "ERROR:\n" + err);
}

Or, using async/await for function sh

const util = require('util');
const execFile = util.promisify(require('child_process').execFile);

async function execute(){
  let result = await sh();
  console.log("RESULT: " + result);
}

async function sh(){
  try {
    const data = await execFile('./demo-files/demo.sh', ['1st', '2nd', '3rd']);
    return data.toString();
  } catch (err) {
    return "ERROR:\n" + err;
  }
}
Bravo
  • 6,022
  • 1
  • 10
  • 15