Chaining of promises with mongodb querys

1

Good morning, I would like to know how to chain promises that have queries to the database (with a callback within the promise).

On the internet I have found that a promise of javascript can be executed in the following way:

function shareCard(req,res){

var SharedCard = new Promise((resolve,reject) => {
    console.log(1);
    resolve(2)
})
.then(element => {
    console.log(element)
    return(3)
})
.then(element => {
    console.log(element)
    return "Salida"
})
.catch((e)=>{
    res.status(500).send({ err: e});
 });

res.status(200).send({Hola : "Hola"})
}

This outputs 123

The problem comes in the following exercise:

function shareCard(req,res){
let idUserSender = req.user._id;

var SharedCard = new Promise((resolve,reject) => {
    console.log(1);
    resolve(2)
})
.then(element => {
    User.findById(idUserSender,(err,userFound) => {
        if (err) {
            reject(err)
        } else {
            console.log(element)
            return(3)
        }
    })
})
.then(element => {
    console.log(element)
    return "Salida"
})
.catch((e)=>{
    res.status(500).send({ err: e});
 });

res.status(200).send({Hola : "Hola"})
}

Where the output to this code is 1 indefinido, 2 In other words, the promise is gaining the callback of the search and just to be able to perform this sequential behavior is that I try to implement promises.

Does anyone have a solution or advice to perform this task?

Thank you in advance.

    
asked by Hitzu Torres 16.05.2018 в 18:23
source

2 answers

0

There are some things in your code that are not quite right, but it is because of the confusion that the promises give.

1. The reject scope is misused

Example

function hacerAlgo(parametro){
   ...
}
// parametro no es accesible desde aquí
console.log(parametro);

Translated to your code

var SharedCard = new Promise((resolve,reject) => {
    // alcance de todas las variables 
    // globales y resolve & reject
    console.log(1);
    resolve(2)

})
.then(element => {
    User.findById(idUserSender,(err,userFound) => {
        if (err) {
            // alcance de todas las variables
            // err & userFound
            // llamar reject dará un error
            reject(err)
        } else {
            console.log(element)
            return(3)
        }
    })
})

2. In the second then you do not return anything in case there is no error in the search (caused by the previous error)

.then(element => {
    User.findById(idUserSender,(err,userFound) => {
        if (err) {
            // alcance de todas las variables
            // err & userFound
            // llamar reject dará un error
            reject(err)
        } else {
            console.log(element)
            return(3)
        }
    })
})

Solution

Move the database call to the body of the promise and use the scope of resolve or reject in the body of the callback of the query

function shareCard(req,res){
    let idUserSender = req.user._id;

    new Promise((resolve,reject) => {
        User.findById(idUserSender,(err,userFound) => {
            if (err) {
                // devolvemos el error
                reject(err)
            } else {
                // devolvemos el usuario encontrado
                resolve(userFound)
            }
        })
    })
    .then(user => {
        // todo fue bien y devolvemos nuestro usuario
        res.status(200).send(user);
    })
    .catch((e)=>{
        // hubo un error
        res.status(500).send({ err: e});
    });
}

To finish I leave you an executable example of promises

(function() {

var print = (message) => document.querySelector('#output').innerHTML += message + '<br>';

  new Promise((resolve, reject) => {
    // simulamos una llamada a la BD con delay
    // tras tres segundos obtendremos la respuesta

    setTimeout(() => {
      // obtenemos un numero aleatorio de 1 a 10
      var result = Math.floor(Math.random() * 10) + 1;
     
      if(result % 2 == 0) {
         // en caso de par resolvemos
        resolve(" [Correcto] - El número " + result + " es par" );
      } else {
       // en caso de impar lanzamos error
        reject(" [Error] - El número " + result + " no es par" );
      }
      
     
    }, 3000);

  })
  .then((message) => print(message))
  .catch((message) => print(message));

})();
<div id="output"></div>
    
answered by 16.05.2018 в 21:26
0

If you want to chain a promise with an API that has callbacks I recommend you use pify or one of the methods of another library of promises like bluebird.promisify . Note that these only work for node style callbacks, that is

callback((error, data) => {})

As for why your code is printing 123 is very simple. You must understand that the promises when they are chained have two channels, the success and the error . A "chain of promises" in the rejected state can "recover" and another in a successful state can be "rejected" by changing the channel in which it is located. It is worth emphasizing that I am not only talking about promises but about the chain of these.

A crucial part of all this is the value you return from the method then

  

When you return a value either in then or% catch it automatically becomes a promise resolved . You can check it in the following snippet (eye you need a compatible browser)

const a = new Promise((resolve, reject) => {
    reject('reason')
  })
  .catch(() => {
    console.log(1);
    return 2;
  })
  .then((val) => {
    console.log(val);
  });

If you notice, I am returning a value within a catch and this became a resolved promise and therefore entered the next then . A very common mistake is to think that returning an error will keep the string in the catch section.

  

If you want your chain to jump to the next catch of the string you should launch an error

const a = Promise
  .reject().catch(() => {
    console.log(1);
    throw new Error();
  }).then(() => {
    console.log(2);
  }).catch(() => {
    console.log(3);
  });

See how it jumps from a catch to the other without going through the then . The same does not happen if you only return an error

const a = Promise
  .reject().catch(() => {
    console.log(1);
    return new Error(1);
  }).then(() => {
    console.log(2);
  }).catch(() => {
    console.log(3);
  });

In this case, print 1 returns a promise results and then jump to then by printing 2, omitting the catch or error channel.

Summing up a string "rejected" can be retrieved in the next step by just returning a value or another string that resolves to anything in its last step and a string " resolved "can be rejected by throwing an error or returning another string that completes in the rejected state.

According to the documentation findById returns a Query that has a method then that makes it a promise. Do not confuse it with the same method then of these. Read this article so you can understand the difference. The method exec if gives you a promise. Instead of this

new Promise((resolve,reject) => {
    User.findById(idUserSender, (err,userFound) => {
        if (err) {
            reject(err)
        } else {
            resolve(userFound)
        }
    });
})

You can only write

return User.findById(idUserSender).exec(); // Esta es una promesa lista para encadenar

As an additional note I will tell you that all this behavior is defined in the spec A + of the promises. The promises of the browsers and node on the server (which are defined in the spec of javascript) are conformant with the A +. There are some promises (very few currently) that do not implement said spec so the behavior could vary but it is usually obsolete libraries.

    
answered by 17.05.2018 в 18:19