{ sailsit }

Sails Js Tutorials for Beginners and Forum

Have a Question?

If you have any question you can ask below or enter what you are looking for!

Callback to Promises

As we all know Sails works on Non-Blocking I/O and due to which callbacks are used.
Callback is invoked on completion of the function block and returns the control back to the calling function.
Callbacks are good for developers who are getting started with SailsJs, as it is easy to understand. But it gets complicated to manage and maintain when you have nested callbacks.
To overcome this nested callbacks issue, promises can be used.
When it comes to promises, it provides cleaner code structuring and it is easy to maintain and debug.
Promises also provides easy error handling, as it has error catching (like try/catch block) in built in promise definition.

Let’s take an example to understand callbacks/promises better

In SailsJs ORM (Waterline) promises are built in with all Model functions (i.e find(), findOne(), …..)

1. Create a SailsJs project

Follow the below tutorial for setting up SailsJs
Setting up sails project

2. MySQL Database Setup

We will be using MySQL DB for this tutorial.
You can follow the below tutorial about setting up MySQL Db

Setting up MySQL in SailsJs

3. Install Packages

In this tutorial we will be using lodash package. Use the below command to install the package

npm install lodash --save

 

3. Creating Models

We will create 3 Models (ClassOne, ClassTwo, Toppers) for this tutorial.
Use the below commands to generate the models

sails generate model ClassOne 
sails generate model ClassTwo 
sails generate model Toppers

 

Add the below model definition to respective models

api/models/ClassOne.js

module.exports = {

    tableName: "class_one",
    migrate: "safe",
    attributes: {
        id: {
            type: "integer",
            primaryKey: true,
            autoIncrement: true
        },
        name: {
            type: "string"
        },
        marks_scored: {
            type: "integer"
        }
    }
};

 

api/models/ClassTwo.js

module.exports = {

    tableName: "class_two",
    migrate: "safe",
    attributes: {
        id: {
            type: "integer",
            primaryKey: true,
            autoIncrement: true
        },
        name: {
            type: "string"
        },
        marks_scored: {
            type: "integer"
        }
    }
};

 

api/models/Toppers.js

module.exports = {

    tableName: "toppers",
    attributes: {
        id: {
            type: "integer",
            autoIncrement: true,
            primaryKey: true
        },
        name: {
            type: "string"
        },
        marks_scored: {
            type: "integer"
        },
        rank: {
            type: "integer"
        }
    }
};

 

4. Creating Controller

Use the below command to generate the controller

sails generate controller Class

 

5. Writing Code

Let’s write a function to sort the students from both the classes and store them in toppers table with their rank

Using Callback

getToppersCallback: function (req, res) {
    ClassOne.find().exec(function (err, classOne) { //fetching from class_one table
       if(err) {
           sails.log.debug(err);
           return res.serverError()
       }
       ClassTwo.find().exec(function (err, classTwo) { //fetching from class_two table
           if(err) {
               sails.log.debug(err);
               return res.serverError();
           }
           var toppers = [];
           toppers = classOne.concat(classTwo);
           toppers = _.orderBy(toppers, ['marks_scored'], ['desc']); //sorting students by marks_scored
           var i=0;
           toppers = toppers.map(function (t) { //assigning rank to students
               delete t.id;
               t.rank = i;
               i++;
               return t;
           });
           Toppers.create(toppers).exec(function (err, toppers) { //storing students with rank in toppers table
               if(err) {
                   sails.log.debug(err);
                   return res.serverError();
               }
               return res.send({
                   "success": true,
                   "message": "Toppers created and added to toppers table"
               });
           });
       });
    });
},

 

Using Promises:

getToppersPromises: function (req, res) {
    ClassOne.find().then(function (classOne) { //fetching from class_one table
            return classOne;
        }).then(function (classOne) {
            return ClassTwo.find().then(function (classTwo) { //fetching from class_two table
                    return {
                        class_one: classOne,
                        class_two: classTwo
                    };
                }).catch(function (err) { //Catch block
                    sails.log.debug(err);
                    throw err; //Goes to final catch block
                });
        }).then(function (classOneTwo) {
            var toppers = classOneTwo.class_one.concat(classOneTwo.class_two);
            toppers = _.orderBy(toppers, ['marks_scored'], ['desc']);
            var i=0;
            toppers = toppers.map(function (t) {
                delete t.id;
                t.rank = i;
                i++;
                return t;
            });
            return Toppers.create(toppers).then(function (t){ //storing students with rank in toppers table
                    return res.send({
                        "success": true,
                        "message": "Toppers created and added to toppers table"
                    });
                }).catch(function (err) {
                    sails.log.debug(err);
                    throw err; //Goes to final catch block
                });
        }).catch(function (err) { //Final Catch Block
            sails.log.debug(err);
            res.serverError();
        });
}

 

As you can see in above codes of callbacks and promises, promises have catch function inbuilt, so any exception go to the catch block and does not crashes the whole application.

Copy the above two functions and paste it into api/controllers/ClassController.js

6. Creating routes

Let’s create routes for these two functions
Copy the below routes and paste in your config/routes.js

'/api/toppers/callback': 'ClassController.getToppersCallback',
'/api/toppers/promises': 'ClassController.getToppersPromises',

 

7. Running application

Now you can run your sails application by running the below command

sails lift

 

You can get the complete source code of this tutorial in below URL
SailsCallbacksToPromises

One Comment

  1. Hello!
    the promises example could be simplified (remember not to fall into the pyramid of doom)
    and if you are using node 8+ it could be even simplier in specific cases.

    Promises are chainable in the sense that whatever you pass on the last return will be used in the next “then” block
    also Exceptions propagate ifen if you have inner promises that don’t have a “catch” block

    getToppersPromises: function (req, res) {
    ClassOne.find()
    .then(function(classOne) {
    // you can send an array of items to the next “then/spread” block
    return [classOne, ClassTwo.find()]
    })
    // using spread you can extract each element of the last array into it’s own object
    .spread(function(class_one, class_two) {
    var toppers = class_one.concat(class_two);
    toppers = _.orderBy(toppers, [‘marks_scored’], [‘desc’]);
    var i = 0;
    toppers = toppers.map(function(t) {
    delete t.id;
    t.rank = i;
    i++;
    return t;
    });
    //storing students with rank in toppers table
    return Toppers.create(toppers);
    })
    .then(function(){
    return res.send({
    “success”: true,
    “message”: “Toppers created and added to toppers table”
    });
    })
    /**
    * As long as you ALWAYS return from the last promise
    * in each “.then/.srpread” block the exception will propagate
    * to the last “.catch” block unless you want to stop the flow of the program
    * you should let exceptions propagate naturally by returing ALWAYS from promises (“then/catch” blocks)
    */
    .catch(function(err) {
    //Final Catch Block
    sails.log.debug(err);
    res.serverError();
    });
    }

    sorry I don’t know how to mark it up

    Hope it helps anyone! Cheers!
    Angel D. Munoz

Comments are closed.