0

So here's the problem. I have a REST API that handles a booking creation, however, before saving the booking inside mongo it validates if there is a clash with another booking.

exports.create = function(req, res) {
    var new_type = new Model(req.body);
    var newBooking = new_type._doc;

    //check if the new booking clashes with existing bookings
    validateBooking.bookingClash(newBooking, function(clash){
        if(clash == null) // no clashes, therefore save new booking
        {
            new_type.save(function(err, type) {
                if (err)
                {
                    res.send(err); //error saving
                }
                else{
                    res.json(type); //return saved new booking
                }
            });
        }
        else //clash with booking
        {
            //respond with "clashDate"
        }
    });
};

Here you have the validation function to check if there is a clash with bookings on the same day:

exports.bookingClash = function (booking, clash) {
    //find the bookings for the same court on the same day
    var courtId = (booking.courtId).toString();

    Model.find({courtId: courtId, date: booking.date}, function(err, bookings) {
        if(err == null && bookings == null)
        {
            //no bookings found so no clashes
            clash(null);
        }
        else //bookings found
        { 
            //for each booking found, check if the booking start hour falls between other booking hours
            for(var i = 0; i<bookings.length ; i++)
            {
                //here is where I check if the new booking clashes with bookings that are already in the DB 
                {
                    //the new booking clashes
                    //return booking date of the clash
                    clash(clashDate); //return the clashDate in order to tell the front-end
                    return;
                }
            }
            //if no clashes with bookings, return null
            clash(null);
        }
    });
};

So, ALL of this works with one single new booking. However, now I want to be able to handle a recursive booking (booking that is made weekly). I have recreated the "create" function and call the validateBooking.bookingClash function inside a for loop.

Unfortunately, when I run this, it calls the bookingClash function perfectly, but when it reaches the line making the search in the database:

Model.find({courtId: courtId, date: booking.date}, function(err, bookings)

It does not wait for the callback and before handling the response "clash", makes i++ and continues.

How can I make it work and wait for the callback?

var array = req.body;
var clashes = [];

for(var i = 0; i<array.length;i++)
     {
         validateBooking.bookingClash(array[i], function(clash)
         {
           if(clash)
           {
               clashes.push(clash);
           }
           else{
              console.log("no clash");
           }
         }
     }

5 Answers 5

1

Seems like a basic async call problem, for loops do not wait for callbacks to be called.

You could use async 'series' function for exmaple instead of the for loop. This way each find will get called after the previous one.

Mongoose also has a promise based syntax which can help you : http://mongoosejs.com/docs/promises.html

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

Comments

1

You Can use async eachSeries

async.eachSeries(users, function iterator(user, callback) {

    if(something) {
        //thing you want to do
        callback();
    } else {

        callback();
    }
}

Comments

1

Since you are using callback functions there are two ways you could try to solve this: 1) use some external library that allows you to perform an asynchronous map operation and run all the checks for each clash. Once they are done check the combined results for a clash and proceed accordingly I would suggest using the async library

your code would look something like: async.map(array,(entry,callback) => validateBooking.bookingClash(entry,callback),(error,mappingResults)=>{...})

2) you could try to change this function to a recursive one

`function recursiveValidation(arrayToCheck,mainCallback){ 
if(arrayToCheck.length === 0) {
   return cb(null} // end of array without errors
} 

validateBooking.bookingClash(_.head(arrayToCheck), function(clash)
     {
       if(clash)
       {
           return mainCallback(clash);
       }

        return  recursiveValidation(_.tail(arrayToCheck),mainCallback);

     }
}`

The above code is just a mockup but it should show the point. The _ is lodash

Comments

1

No need to changing anything in your code except the declaration use let instead of var and your loop should work.

var array = req.body; var clashes = [];

`
for(**let** i = 0; i<array.length;i++)
     {
         validateBooking.bookingClash(array[i], function(clash)
         {
           if(clash)
           {
               clashes.push(clash);
           }
           else{
              console.log("no clash");
           }
         }
     }`

You have to understand the difference between let and var. Also why var cannot be used for running async code inside a loop. Learn about let: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/let

Comments

0

I found the way to get this done after trying all of your answers.

What I had to do was this:

validateBooking.singleBooking(new_type._doc, newBookingClubId, function (clash) {
                if (clash == null) // no clash
                {
                    validatorArray.push(0);
                    if(validatorArray.length == array.length) //has received everything from mongo
                    {
                        console.log("Clashes: " + clashes.toString());
                        if(validatorArray.indexOf(1) > -1) //contains a clash
                        {
                            var error = {
                                code: 409,
                                message: "409 Conflict",
                                clashes: clashes
                            };
                            errorsHandler.handleError(error, res);
                        }

This way, I created an array called "validatorArray" that was called every time I received something back from Mongo.

This way I could easily compare the length of the array of bookings and the validatorArray length. When they were equal, it meant that it had received everything back from mongo and could send back the response.

Thanks for the help!

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.