3

Using ES6 Promises, how can I break it in the following scenario?

addClient: function(request, response, next) {
    var id = mongo.validateString(request.body.id);

    mongo.Test.findOne({
        id: id
    })
    .then(client => {
        if (client) {
            // want to break here
            response.status(400).send({
                error: 'client already exists'
            });
        } else {
            return auth.hashPassword(mongo.validateString(request.body.secret));
        }
    })
    .then(hashedSecret => {
        // gets executed even if I don't return anything
        return new mongo.Test({
            name: mongo.validateString(request.body.name),
            id: id,
            secret: hashedSecret
        }).save();
    })
    .then(doc => {
        return response.status(201).send({
            client: {
                id: doc._id
            }
        });
    })
    .catch(err => {
        return next(err);
    });
}

I haven't found any clear docs stating how to break this. Instead of having chained thens I could have it inside the first then but on more complex requests it would be nice to be able to have them chained.

Mukesh Sharma
  • 8,914
  • 8
  • 38
  • 55
hpinhal
  • 512
  • 6
  • 22
  • I'm not sure this is possible in a clean, canonical way. See [this answer](https://stackoverflow.com/questions/29478751/how-to-cancel-an-emcascript6-vanilla-javascript-promise-chain) for something similar. I've done this in the past by either returning `null` through each `then`, which is horrifyingly messy; or throwing when I want to break and testing for that in the `catch` statement, which, again, feels hacky. – Fissure King Jan 22 '17 at 20:26

3 Answers3

0
// gets executed even if I don't return anything
return new mongo.Test({
      name: mongo.validateString(request.body.name),
      id: id,
      secret: hashedSecret
    }).save();

This gets executed even if you don't return anything because the default return value of such thing is undefined. It is assumed that code resolved with undefined

If this value is not a promise, including undefined, it becomes the fulfillment value of the associated promise

Mozilla docs on this

You can break a Promise chain by rejecting it early.

addClient: function (request, response, next) {
  var id = mongo.validateString(request.body.id);
  mongo.Test.findOne({
    id: id
  }).then(client => {
    if (client) {
      // reject with a type
      return Promise.reject({
        type: 'CLIENT_EXISTS'
      });
    }
    return auth.hashPassword(mongo.validateString(request.body.secret));
  }).then(hashedSecret => {
    // gets executed even if I don't return anything
    return new mongo.Test({
      name: mongo.validateString(request.body.name),
      id: id,
      secret: hashedSecret
    }).save();
  }).then(doc => {
    return response.status(201).send({
      client: {
        id: doc._id
      }
    });
  }).catch((err) => {
    // check type
    if (err.type === 'CLIENT_EXISTS') {
      return response.status(400).send({
        error: 'client already exists'
      });
    }
    return next(err);
  });
}
Sridhar
  • 11,466
  • 5
  • 39
  • 43
0

If you want to break a promise chain aka not execute the following chained promise links you can inject a unresolved promise into the chain by returning a promise that will never resolve.

new Promise((resolve, reject) => {
    console.log('first chain link executed')
    resolve('daniel');
}).then(name => {
    console.log('second chain link executed')
    if (name === 'daniel') {
        return new Promise(() => {
            console.log('unresolved promise executed')
        });
    }
}).then(() => console.log('last chain link executed'))




// VM492:2 first chain link executed
// VM492:5 second chain link executed
// VM492:8 unresolved promise executed
DanLatimer
  • 117
  • 8
0

I break the chain by returning a new Promise which is rejected directly inn the same line with a certain error message or object.

Due to that I get the error in the last catch block and return my response in a single place.

betHelper.getPrediction(db, match_id, _userID)
    .then(function (prediction) {
        if (prediction != null) {
            return Promise.reject({error: "You already have a bet for this match", prediction: prediction});
        } else {
            //Prediction does not exist find user and match data
            return Promise.all([userHelper.findUserWithID(_userID, db), betHelper.getMatchData(match_id)]);
        }
    })
    .then(function (results) {
        let user = results[0];
        let game = betHelper.simplifiedMathData(results[1]);
        //Append user prediction
        return new Promise(betHelper.appendPrediction(db, user, game, localteam_score, visitorteam_score));
    })
    .then(function (prediction) {
        response.send(prediction);
    })
    .catch(function (err) {
        response.send(err);
    });
Ilker Baltaci
  • 11,644
  • 6
  • 63
  • 79