1

I am using nodeJS and mongoDB.

I coded a function to get the biggest index inside my database to be able to use it when I post a new element in the database.

I created a function to insert a element in my database :

function postComparison(){

console.log("Titre : " + document.getElementById("Titre").value);

var myHeaders = new Headers();
myHeaders.append("Content-Type", "application/json");

var raw = JSON.stringify({
  "_id": getLastIndice()+1, // HERE
  "id_User": "2",
  "PseudoUser": "bob",
"Titre" :  document.getElementById("Titre").value
});

var requestOptions = {
  method: 'POST',
  headers: myHeaders,
  body: raw,
  redirect: 'follow'
};

fetch("http://localhost:3000/comparaison", requestOptions)
  .then(response => response.text())
  .then(result => console.log(result))
  .catch(error => console.log('error', error));}
</script>

I am calling the getLastIndice() function to increment the index from the last index I have in my database.

The function getLastIndice() call the router :

function getLastIndice(){
    console.log("on appel la fonction get LastIndice");
      var requestOptions = {
      method: 'GET',
      redirect: 'follow'
      };

  fetch("http://localhost:3000/comparaison/getLastIndice", requestOptions)
  .then(response => response.text())
  //.then(result => document.getElementById("textRepBDD").innerHTML = result)
  .catch(error => console.log('error', error));
  console.log('appel de la fonction qui recupere le dernier indice avec comme réponse : ' + response );
  return parseInt(response);
}
</script>

And inside the router I have :

comparaisonRouter.route('/getLastIndice')

.get( (req, res, next) => {
        var MongoClient = require('mongodb').MongoClient;
        var url = "mongodb://localhost:27017/";

        MongoClient.connect(url, function(err, db) {
        if (err) throw err;
        var dbo = db.db("siteComparaisonMax");
        //Sort the result by name:
        var sort = { _id: -1 }; // - 1 Pour trier dans un ordre decroissant.
        dbo.collection("comparaisons").find().sort(sort).limit(1).toArray(function(err, result) {
            if (err) throw err;
            //console.log(JSON.parse(result._id));
            console.log(result[0]._id);
            res.json(result[0]._id);
            db.close();
        });
        });

    })

The function get last index is working but when I'm calling the postComparaison() function it just calling the getLastIndice() and that's all.

I think that I'm missing a next() somewhere.

1 Answer 1

1

To return a value from a promise you use it's .then(). The .then() method is not there for you to log the result. It is there for postComparaison() to call to get the result:

function getLastIndice(){
    console.log("on appel la fonction get LastIndice");
    var requestOptions = {
        method: 'GET',
        redirect: 'follow'
    };

    // IMPORTANT! The `return` below in front of the `fetch()` is
    // what will return the value to the caller:
    return fetch("http://localhost:3000/comparaison/getLastIndice", requestOptions)
      .then(response => response.text())
      .then(result => {
          console.log('code INSIDE THIS THEN executes AFTER we get response : ' + result );
          return parseInt(result);
      })
      .catch(error => console.log('error', error));

      console.log('code OUTSIDE THE THEN ABOVE executes BEFORE we get response');
}

This is very important. Code at the bottom of the function executes FIRST. Code inside the then executes LAST.

To clarify let's remove all details from the function:

function getLastIndice() {
    // THIS HAPPENS FIRST
    console.log('first');        

    return fetch()
      .then(response => response.text())
      .then(result => {
          // THIS HAPPENS THIRD
          console.log('third');
          return parseInt(result);
      });

       // THIS HAPPENS SECOND
      console.log('second');
}

The above code will log:

first
second
third

But please note:

function getLastIndice() {

    return fetch() // THIS RETURN IS IMPORTANT ! !

      .then(r => r.json())
      .then(r => parseInt(r));
}

So basically a clean version of getLastIndice() can be written simply as:

function getLastIndice() {

    var requestOptions = {
        method: 'GET',
        redirect: 'follow'
    };

    return fetch("http://localhost:3000/comparaison/getLastIndice", requestOptions)
      .then(x => x.text())
      .then(y => parseInt(y));

}

That's all the function needs to be. We don't really need (or want) to catch the error here because we want the caller to be able to detect the error so we simply remove the .catch().

Now that the function works properly here's how you use it in postComparison():

function postComparison() {
    var myHeaders = new Headers();
    myHeaders.append("Content-Type", "application/json");

    // Remember, .then() is how getLastIndice() RETURNS its value:
    getLastIndice().then(indice => {
    
        var raw = JSON.stringify({
            "_id": indice+1, // WE GOT THIS FROM .then of getLastIndice()
            "id_User": "2",
            "PseudoUser": "bob",
            "Titre" :  document.getElementById("Titre").value
        });

        var requestOptions = {
            method: 'POST',
            headers: myHeaders,
            body: raw,
            redirect: 'follow'
        };

        fetch("http://localhost:3000/comparaison", requestOptions)
            .then(response => response.text())
            .then(result => console.log(result))
            .catch(error => console.log('error', error));
   }
}

Alternatively you can return the variable raw and process the rest inside another .then(). That's the whole reason Promises were invented - to flatten callback nesting by chaining .then()s:

function postComparison() {
    var myHeaders = new Headers();
    myHeaders.append("Content-Type", "application/json");

    // Remember, .then() is how getLastIndice() RETURNS its value:
    getLastIndice()
        .then(indice => {
            var raw = JSON.stringify({
                "_id": indice+1,
                "id_User": "2",
                "PseudoUser": "bob",
                "Titre" :  document.getElementById("Titre").value
            });

            return raw; // return this to the `then` below:
        })
        .then(requestBody =>
            var requestOptions = {
                method: 'POST',
                headers: myHeaders,
                body: requestBody, // requestBody comes from raw above
                redirect: 'follow'
            };

            return fetch("http://localhost:3000/comparaison", requestOptions);
        })
        .then(response => response.text())
        .then(result => console.log(result))
        .catch(error => console.log('error', error));

        // ^^ NOTE: all the then() are on the same level!
}

If you want postComparison() to return the final result just do the same thing we did with getLastIndice() above: add the return keyword in front of getLastIndice():

function postComparison() {
    var myHeaders = new Headers();
    myHeaders.append("Content-Type", "application/json");

    return getLastIndice() // THIS RETURN IS IMPORTANT !
        .then(indice => {
            var raw = JSON.stringify({
                "_id": indice+1,
                "id_User": "2",
                "PseudoUser": "bob",
                "Titre" :  document.getElementById("Titre").value
            });

            return raw;
        })
        .then(requestBody =>
            var requestOptions = {
                method: 'POST',
                headers: myHeaders,
                body: requestBody, // requestBody comes from raw above
                redirect: 'follow'
            };

            return fetch("http://localhost:3000/comparaison", requestOptions);
        })
        .then(response => response.text())
        .then(result => {
            console.log(result);
            return result; // return the result into the chain of then()
                           // this return is ALSO IMPORTANT
         })
        .catch(error => console.log('error', error));
}

An Analogy:

Using Promises you ALWAYS have to remember that the code inside then() happens after the end of the function, not before! It actually makes sense if you think about it.

Say for example you are ordering a Pizza. What usually happens is something like this:

  1. You call Restaurant() and order a pizza
  2. pizzaDeliveryGuy(() => will send you the pizza)
  3. You put down the phone
  4. You wait for the pizzaDeliveryGuy()

Note that in the events above the pizzaDeliveryGuy() is the .then() and the Restaurant() makes a Prmoise to send you a pizza (using pizzaDeliveryGuy()). Even though the Restaurant() promised you to deliver the pizza BEFORE you put down the phone the send you the pizza event happens AFTER you put down the phone.

Your confusion is exactly this: not understanding that a promise happens in the future. The pizza delivery guy will send you the pizza after you make the order even though the restaurant promised you the pizza before you put down the phone. Similarly, Promises will execute the .then() callback after the end of the function, not before.

Modern code with async / await:

ECMAScript version 6 introduced two keywords that helps simplify handling asynchrnous code: async and await. They build upon Promise and is basically a special syntax for inlining promises.

Basically if you write something like this:

async function x () {
    let y = await z();
    console.log(y);
}

Javascript will compile it to something like this:

function x () {
    z().then(y => console.log(y));
}

Using async/await we can simplify your code. First we can rewrite getLastIndice() like this:

async function getLastIndice() {

    var requestOptions = {
        method: 'GET',
        redirect: 'follow'
    };

    let response = await fetch("http://localhost:3000/comparaison/getLastIndice", requestOptions);

    let txt = await response.text();

    return parseInt(txt);
}

Because we are using await everything in the function above happens in sequence from top to bottom:

async function getLastIndice() {
    // THIS HAPPENS FIRST
    var requestOptions = {
        method: 'GET',
        redirect: 'follow'
    };

    // THIS HAPPENS SECOND    
    let response = await fetch(URL, requestOptions);

    // THIS HAPPENS THIRD
    let txt = await response.text();

    // THIS HAPPENS LAST
    return parseInt(txt);
}

Now we can rewrite postComparison() like this:

async function postComparison() {
    var myHeaders = new Headers();
    myHeaders.append("Content-Type", "application/json");

    try {

        let indice = await getLastIndice();

        var raw = JSON.stringify({
            "_id": indice+1,
            "id_User": "2",
            "PseudoUser": "bob",
            "Titre" :  document.getElementById("Titre").value
        });

        var requestOptions = {
            method: 'POST',
            headers: myHeaders,
            body: raw
            redirect: 'follow'
        };

        let response = await fetch("http://localhost:3000/comparaison", requestOptions);

        let result = await response.text())

        console.log(result);
        return result;
    }
    catch(error) {
        console.log('error', error);
    }
}

As you can see, async/await is much easier to read for simple sequential code like this. Just don't forget to await the results.

Still, even if you use async/await I think it's critical to understand how Promises work and how .then() work because async/await was built on top of promises. There are some situations where you want two or more things to run in parallel instead of sequentially and that is when you need to go back to promises and callbacks.

Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.