-2

Here is the code I'm using:

const { exec } = require("child_process");

exec("ls -la", (error, stdout, stderr) => {
    if (error) {
        console.log(`error: ${error.message}`);
        return;
    }
    if (stderr) {
        console.log(`stderr: ${stderr}`);
        return;
    }
    console.log(`stdout: ${stdout}`);
});

It returns "0" and then logs in the console the correct result.

But how to directly return the result instead of console.log?

Some answers have pointed out how to convert it into a Promise.

  • return it where? How do you call it? the `exec` runs asynchronously so you'd have to use promises or async/await – Garr Godfrey Sep 20 '21 at 17:52
  • You can't, this it's async. The value can only be used in the callback. – zero298 Sep 20 '21 at 17:52
  • 2
    Does this answer your question? [How to return the response from an asynchronous call](https://stackoverflow.com/questions/14220321/how-to-return-the-response-from-an-asynchronous-call) – zero298 Sep 20 '21 at 17:53
  • @GarrGodfrey, I call it using `Browser.ExecJS("")` in an IDE. It returns "0" for then logging the correct result. It should use a promise to directly return the correct result. – dani 'SO learn value newbies' Sep 20 '21 at 17:58
  • Nice, @zero298. But how to adapt it to my code? If you answer, I probably will mark it as the correct answer if it works. – dani 'SO learn value newbies' Sep 20 '21 at 17:59
  • Take a look at this answer in the linked duplicate, it shows how to wrap into a `Promise`: https://stackoverflow.com/a/44212447/691711 Search for "If the underlying function is not promisified". This question is asked very often and I'd rather not create yet another duplicate when there is already an answer that solves your issue. – zero298 Sep 20 '21 at 18:01
  • This is going to depend on exactly what your IDE supports. Your javascript function MUST return before it know the result, so the IDE would need to have a callback mechanism (a way to call from javascript to the IDE), UNLESS it already supports await – Garr Godfrey Sep 20 '21 at 19:07

2 Answers2

1

You can't return a value from that callback, because it would not be passed to anything. What you can do is defining a Promise that passes the stdout to the resolve method. Here's an example:

    const { exec } = require("child_process");
    function ls() {
        return new Promise((resolve, reject) => {
            exec("ls -la", (error, stdout, stderr) => {
                if (error) {
                    console.log(`error: ${error.message}`);
                    reject(error.message);
                }
                if (stderr) {
                    console.log(`stderr: ${stderr}`);
                    reject(stderr);
                }
                console.log(`stdout: ${stdout}`);
                resolve(stdout);
            });
        });
    }

What I am doing here is defining a function that creates a new Promise. The Promise will execute your code (the ls -la call), and will fire an exception if there is an error, rejecting the Promise, or it will solve the Promise if everything is fine, passing the stdout value.

You can then use this Promise with something like this:

ls().then((out) => {
    console.log(out);
})

the out variable will contain your stdout.

If you want some function that returns that value, it should be awaited from this function. An example could be this:

async function unwrapLs() {
    const stdout = await ls();
    return stdout;
}

Note that you can only call unwrapLs() from inside an async function, because you have to await for its value. In fact, this would be equivalent to calling ls() by awaiting it, but you can only do it from inside an async function.

Gregorio Palamà
  • 1,965
  • 2
  • 17
  • 22
  • It works. But what is missing: it should directly return instead of logging on console. If you update it and it works, it will be the correct answer. – dani 'SO learn value newbies' Sep 20 '21 at 19:01
  • you could try `Browser.ExecJS("await ls()")` but no way to know it will work – Garr Godfrey Sep 20 '21 at 19:08
  • @GarrGodfrey, it throws a error: "c2runtime.js:16438 Error executing Javascript: SyntaxError: Unexpected identifier" – dani 'SO learn value newbies' Sep 20 '21 at 19:16
  • `ls().then((out)=>{console.log(out)})` works like a charm BUT shouldn't use console.log (as of the point of this question), but directly return or something alike. – dani 'SO learn value newbies' Sep 20 '21 at 19:18
  • Returning a value is not the scope of a promise's callback. In fact, my suggestion is to encapsulate the exec() in a promise, so you can use the callback or the async/await mechanism. Returning the value inside the then() will only result in returning a promised value, that will need to be observed, making it needed to use an other .then(). – Gregorio Palamà Sep 20 '21 at 19:26
  • `const{exec}=require(""child_process"");function ls(){return new Promise((resolve,reject)=>{exec(""ls -la"",(error,stdout,stderr)=>{if(error){console.log(`error:${error.message}`);reject(error.message)}if(stderr){console.log(`stderr:${stderr}`);reject(stderr)}console.log(`stdout:${stdout}`);resolve(stdout)})})}ls()` have worked, by just using `ls()` instead of `await ls()` BUT still using the infamous `console.log` instead of directly `return`ing. At least we are getting closer to an answer. – dani 'SO learn value newbies' Sep 20 '21 at 19:27
  • 1
    Again: there is no way to return a value from inside a Promise without having to await for it. It would lead to a useless wrapping of Promises. One other way is to create an async function that awaits the Promise for you, and it could return the value. I'll update the answer with this example – Gregorio Palamà Sep 20 '21 at 19:30
  • @GregorioPalamà I'm getting into a solution, but no result yet. I've used the 3 JS quotes of your answer, in the same order; but got no return result. For instance, `function addressbarvalue(){var x=document.getElementById('addressbar').value;return x}addressbarvalue()` returns correctly. How to use the last two quotes of your answer properly? If we get it to work, it means your answer is the correct one – dani 'SO learn value newbies' Mar 08 '22 at 00:03
  • I've also tried `ls().then((out)=>{console.log(out);return out;})` but it didn't returned. – dani 'SO learn value newbies' Mar 08 '22 at 00:07
1

The problem you are experiencing is that it takes time to run an OS command (perhaps indefinite amount of time). Javascript will not just halt execution until this is done, so it is run asynchronously.

A value must be returned and must be returned to the caller before it completes. Using promises, the value returned is a promise.

Since you are calling with the Construct 2 Engine which does not have a mechanism for asynchronous calls, you will not get a result directly.

However, Construct 2 engine has a mechanism for calling back into it, using the Function object

Your javascript should look like this:

const { exec } = require("child_process");

function ls(callback) {
    exec("ls -la", (error, stdout, stderr) => {
        if (c2_callFunction)
            c2_callFunction(callback, [error, stdout, stderr]);
    });
}
ls('lsresult')

you can execute like this :

Browser.ExecJS("")

But, to get the results, you must define a Function object called 'lsresult', add parameters to the function (error, stdout, stderr) and handle the results there.

Documentation is here: https://www.construct.net/en/construct-2/manuals/construct-2/plugin-reference/function

Garr Godfrey
  • 8,257
  • 2
  • 25
  • 23