user11527868
user11527868

Reputation:

NodeJS - how to control execution flow of promises?

I am asking this to try and understand nodeJS.. I'm trying to use the following code to execute CMD opreations one by one, but due to the event loop (i assume) it gets executed in random variations. This is my code:

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

//os_func's execCommand is incharge of executing cli strings using a promise
function os_func() {
    this.execCommand = function (cmd) {
        return new Promise((resolve, reject)=> {
           exec(cmd, (error, stdout, stderr) => {
             if (error) {
                reject(error);
                return;
            }
            resolve(stdout)
           });
       })
   }
}

//start
var os = new os_func();

os.execCommand('pwd').then(res=> {
    console.log("os1 >>>", res); //sometimes first, sometimes second
}).catch(err=> {
    console.log("os >>>", err);
})

os.execCommand('pwd').then(res=> {
    console.log("os2 >>>", res); //sometimes first, sometimes second!
}).catch(err=> {
    console.log("os >>>", err);
})

How can I control the execution-flow, while not affecting performance?

Upvotes: 1

Views: 197

Answers (1)

Nino Filiu
Nino Filiu

Reputation: 18493

First off, the JS way to make (err, ...args)-based callbacks asynchronous is to use util.promisify that was built just for this purpose. Example:

const {exec} = require('child_process');
const {promisify} = require('util');
const exec_async = promisify(exec);
exec_async('pwd').then(({stdout, stderr}) => console.log(stdout))
// you can use exec_async instead of os.execCommand

Then, you have to understand that promises are asynchronous. To make it simple, between the invocation and the resolving of a promise, things can happen. Asynchronicity is one of the building blocks of JS - I won't go into details on it, but if you have troubles understanding this concept I'd recommend starting by MDN's guide on asynchronous programming in JS.

Basically in your code, this can happen:

time
|
| os.execCommand('echo hello from 1')
| .then(res=> console.log('os1 >>> ', res))
| os.execCommand('echo hello from 2')
| .then(res=> console.log('os2 >>> ', res))
V
Output:
os1 >>> hello from 1
os2 >>> hello from 2

But also this:

time
|
| os.execCommand('echo hello from 1')
| os.execCommand('echo hello from 2')
| .then(res=> console.log('os2 >>> ', res))
| .then(res=> console.log('os1 >>> ', res))
V
Output:
os2 >>> hello from 2
os1 >>> hello from 1

And also a few other spooky combinations. If you want to make sure that one command executes before another one, you should instead chain your promises using then:

exec_async('echo hello from 1')
.then(console.log)
.then(
  exec_async('echo hello from 2')
  .then(console.log)
)

Nested then can affect readability, as such you may opt for the async/await syntax:

const hello1 = await exec_async('echo hello1');
const hello2 = await exec_async('echo hello2');

Upvotes: 1

Related Questions