admin管理员组

文章数量:1392076

I'm using a Mongoose query like this:

let Value = 'START'
myCollection.findOne({param:Value}) .then((doc) => {
    if (!doc) {
        // In this case no document found on my collection.
        // I need to log this case with the parameter 'Value' used for the query.
        // In my example it should be 'START'.
        log(Value) // => Sometimes Value is 'END' due to the asyn and the query delay.
    }
})
Value = 'END'

Sometime, my query return no value. This is not an error but I need to log the query in this case. Unfortunately, the doc parameter is null. Because it is run async, the "Value" has changed when I enter the then function and I cannot use my "Value" variable.

How can I get the param value inside the then function ?

I'm using a Mongoose query like this:

let Value = 'START'
myCollection.findOne({param:Value}) .then((doc) => {
    if (!doc) {
        // In this case no document found on my collection.
        // I need to log this case with the parameter 'Value' used for the query.
        // In my example it should be 'START'.
        log(Value) // => Sometimes Value is 'END' due to the asyn and the query delay.
    }
})
Value = 'END'

Sometime, my query return no value. This is not an error but I need to log the query in this case. Unfortunately, the doc parameter is null. Because it is run async, the "Value" has changed when I enter the then function and I cannot use my "Value" variable.

How can I get the param value inside the then function ?

Share Improve this question edited Mar 13 at 8:47 DarkBee 15.5k8 gold badges72 silver badges118 bronze badges asked Mar 11 at 16:54 DarkScytaleDarkScytale 11 bronze badge 2
  • 1 This is very simple to solve with async/await pattern or by moving the reassignment of Value into the the then block. Someone can give you a "make it work" answer but can you explain why Value is being set to END? That might help someone give you an answer with more meaning. – jQueeny Commented Mar 11 at 20:14
  • The values 'START' and 'END' are just for the example. If you try the example, node enters the "then" function far far after the 'Value='END' is executed. Concerning the await, if I do it, I loose completely the speed capability of my code. – DarkScytale Commented Mar 13 at 8:37
Add a comment  | 

2 Answers 2

Reset to default 0

Asynchronous JavaScript functions such as your myCollection.findOne stop the blocking of the Node.js single thread and allows it to continue execution of the code in your script. This is a big topic that you should spend time reading and learning. That means your Value = 'END' was executed before the result of your database query was returned. The JavaScript engine continued to execute synchronous code you had in the lines immediately after the then function. So when you actually come to logging the value with log(Value), the value of Value was already reassigned to 'END'.

When you read your code you are reading it line by line and intuitively you see that log(Value) is above the Value = 'END' so how can it have changed already?

To keep things simple, your myCollection.findOne has to issue a network request to the server running your database, query the database, then return the results back across the network to your server. That might involve disk I/O and a high latency network which all takes time. Asynchronous functions allow you delegate that execution similar to giving your order to a waiter. The waiter can send that order to the kitchen and continue serving you drinks. The kitchen will let the waiter know when your order is ready and bring it to you. You shouldn't have to wait for your food before you enjoy your drink . The then() block is like the kitchen telling the waiter the order is ready. By which time you have different drink than the one you had when you placed the order.

There is an alternative pattern to using then blocks and that is async/await. In this case using the await keyword before the myCollection.findOne tells the JavaScript engine to pause execution of the rest of the code in your script until the the request is settled (fulfilled or rejected). So essentially I'm happy to wait for my food order on this occasion as the rest of my evening depends on it. Crucially though it also doesn't block the Node.js single thread and carries on responding to requests - just not those lines of code directly after the await myCollection.findOne. Again, it's a very large topic involving promises and error handling that I won't go into.

1. Solution with then():

let Value = 'START'
const valueSaved = Value;
myCollection.findOne({param:Value}).then((doc) => {
    if (!doc) {
        console.log(valueSaved)
        // 'START' 
    }
})
Value = 'END'

Simply assign Value to another variable which you then reference inside the then() block.

2. Solution with async/await:

app.get('/', async (req, res) => { //< function must be async
   try {
      let Value = 'START';
      const doc = await myCollection.findOne({param:Value}); //< use await here
      // Code will not continue until settled
      // ...    
      if(!doc){
         console.log(Value)
      }
      let Value = 'END';

      res.status(200).json({
         doc: doc
      });

    } catch(err) {
       console.log(err)
       res.status(500).json({
          msg: 'Error on server'
       });
    }
});

The await keyword must be used inside a function that is marked as async such as the callback to a router etc. (top-level await also possible)

The issue is due to JavaScript's asynchronous nature. When the .findOne() query executes, the value of Value might have already changed before the .then() callback runs.

To preserve the value of Value at the time of the query, you need to capture it within the .findOne() call:

let Value = 'START';

myCollection.findOne({ param: Value }).then((doc) => {
    if (!doc) {
        console.log(`No document found for query: ${Value}`);
    }
});


Value = 'END';

本文标签: nodejsHow to get the query from the then functionStack Overflow