Home > Net >  async function on route.js file | nodejs
async function on route.js file | nodejs

Time:12-30

I have an application that it has nodejs as backend and some scripts in Python

The problem is to make the 'PythonShell' (function to access the scripts) as a async function. I do not know why but it is not working.

I'll put the code from my router.js file and inside of it I put three 'console.log('steps')' to check the sequences.

It should be Step01 > Step02 > Step03, but as it is not working, It always prints Step01 > Step03 > Step02

Everything is working fine, except for this async problem! For me it should work as it is.

How can I edit my functions to execute first the 'goToscript/PythonShell' and then execute 'res.json(responseScript)'?

Thanks

   router.put("/uploads/script-03", async (req, res) => {

    let options = {
        scriptPath: "scripts",
        args: JSON.stringify(req.body)
    };

    const goToScript = async () => {
        
        await PythonShell.run("script-01.py", options, (err, res) => {
            if (err) {
            }
            if (res) {
                responseScript = JSON.parse(res)
                console.log('Step 02')
            }
        });
    }

    console.log('Step 01')

    goToScript()
    
    console.log('Step 03')
    
    res.json(responseScript)
});

module.exports = router

CodePudding user response:

A couple things:

1. Your goToScript is not actually async/returning a Promise

From what I can tell, PythonShell doesn't support async, only callbacks, so you can rewrite your gotToScript like so:

const goToScript = () => {
  return new Promise((resolve, reject) => {
    PythonShell.run("script-01.py", options, (err, res) => {
      if (err) {
        reject(err)
      }

      if (res) {
        responseScript = JSON.parse(res)
        console.log('Step 02')

        resolve(responseScript)
      }
    })
  })
}

const scriptResult = await goToScript()

This code will work like a regular async function, where the promise will resolve to the parsed JSON, and reject with the error if it meets one.

2. You are not awaiting your call to goToScript

When you want to make an async call that finishes in sequence with everything else, you need to await it. Take these two examples:

In this first chunk of code, waitFn waits before 100ms before logging "Step 2!":

const waitFn = () => {
  return new Promise((resolve) => {
    setTimeout(() => {
      console.log('Step 2!')
      resolve()
    }, 100)
  })
}

console.log('Step 1!')
waitFn()
console.log('Step 3!')

Because you do not await the result of the Promise, your code doesn't care that is has not finished, and will print:

Step 1!
Step 3!
Step 2!

Instead, however, if you await the result of the Promise returned in waitFn, it will execute in order:

const waitFn = () => {
  return new Promise((res) => {
    setTimeout(() => console.log('Step 2!'), 100)
  })
}

console.log('Step 1!')
await waitFn() // Finishes before moving on
console.log('Step 3!')

You can read a bunch more about Promises and async/await here :)

CodePudding user response:

To be able to await function - this function needs to return a promise. In the npm page of your lib there is a description, about what PythonShell.run returns. It does not return a promise. So it is asynchronous, but not awaitable, it is callback based.

All you need to do - to promisify this function. Additionaly - you need to await the call to goToScript();.

router.put("/uploads/script-03", async (req, res) => {
  let options = {
    scriptPath: "scripts",
    args: JSON.stringify(req.body)
  };

  const goToScript = async () => {
    return new Promise((resolve, reject) => {
      PythonShell.run("script-01.py", options, (err, res) => {
        console.log("Step 02");
        if (err) return reject(err);
        return resolve(JSON.parse(res));
      });
    });
  };

  console.log("Step 01");
  const responseScript = await goToScript();
  
  console.log("Step 03");
  res.json(responseScript);
});

module.exports = router;
  • Related