简体   繁体   中英

Async function not behaving as expected in Nodejs

I have this simple code:

(async ()=>{
    const five = await printHello()
    console.log(`five: ${five}`);
})()

async function printHello(){
    console.log('Hello World')
    return 5;
}

console.log("App started");

My expectation is that it should print :

App started    
Hello World
five: 5

As the anonymous function that gets executed is marked async.

However it prints, even after multiple trials :

Hello World
App started
five: 5

Can some one please explain me why this is happening ?

First, you call printHello() .

Then you call console.log('Hello World') which logs.

Then return 5; which resolves the promise.

Then you const five = await ... which awaits the promise returned above. This is a microtask so it gets queued up and the async function goes to sleep.

The IIFE finishes running, so the outer function continues with console.log("App started"); which logs App started.

Then the outer function finishes, so the microtask is pulled off the queue and the async function wakes up.

The resolved value from the promise is assigned and console.log(`five: ${five}`); logs it.

See Timing from MDN for further reading.

async function returns a promise but the synchronous code inside async function is executed immediately (ie synchronously). await statement pauses code execution until the promise which is given to it is resolved or rejected.

I always compare async function behaviour with the Promise 's executor param behaviour. executor function is called immediately ie synchronously.

Your code is not doing anything asynchronous. The printHello method executes synchronously. Prefixing a function with async just make sure that it returns a Promise. You could replace it with callback and it will work the same way, as callback will return immediately without waiting for the next tick of the event loop.

So if you want your code to behave the way you want it to, then you will have to wrap it around with a setTimeout or setImmediate call.

async function printHello() {
      setImmediate(() => {
        console.log('Hello World');
        return 5;
      });
  }

Your code is not asynchronous. That is this the problem. You need to return a promise that is always asynchronous.

const users = [
  {
    id: 0,
    name: 'foo',
  },
  {
    id: 1,
    name: 'bar',
  }
];

function getUserById(id) {
  return new Promise((resolve, reject) => {
    setTimeout(function() {
      resolve(users.filter(user => user.id === id)[0]);
    }, 1000);
  });
}

async function getUser(id) {
  let name;

  await getUserById(id).then(function(user) {
    name = user ? user.name : null;
  });

  console.log(name);
}

getUser(0);
console.log('console');

All guys have given plenty helpful answer, but I will need to write an answer for this to help people who come up with a similar mentality as me : Just by marking a function async [or wrapping it in Promise] will not make your CPU intensive code to run as asynchronous fuct in nodejs.

My understanding prior to this problem, was that we can just put async before a function and put all heavy task on it. Aysnc will some how let nodejs event loop know once its finished [returned]. And I use to think it does this all things under the hood via spawning threads.

But this understanding is incorrect. Node is single threaded, if you ever going to write your code. Do never ever put any thing heavy task on nodejs.

Consider this express app:

const express = require('express')

const app = express()

app.use('/name/:name',(req,res)=>{
    if(req.params['name']==='John'){
        res.end(timeConsumingFunct(2000).toString())
    }else{
        res.end(timeConsumingFunct(10000).toString())
    }
})


function timeConsumingFunct(ms) {//simulating CPU intensive work
    var start = Date.now(),
        now = start;
    while (now - start < ms) {
      now = Date.now();
    }
    return ms/1000;
}

app.listen(3000,()=>{
    console.log('Server started at : http://localhost:3000')
})

Now if you access : http://localhost:3000/name/Tom first and then http://localhost:3000/name/John in your browser, Mr.John will need to wait for total of 12 seconds minimum. These is because Node js is single threaded no matter you use async or a Promise to wrap your timeConsumingFunct it will hung up entire flow.

The only way to make it truly async will be to use child process or worker api or write it in C++ .

Whatever you write as a JS is always synchronous and its going to hang up all the requests, know that before you think you can make any thing aysnc in nodejs.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM