Express js hangs on heavy function till its done

I have a loop function that I am testing for one of my routes. The problem is when I call it it hangs all web calls till its done with the function. I wanted to know how to not lock up node while its processing this.

app.get('/populate', routes.populate);

exports.populate = function(req, res, next){

    for(i = 0; i < 100000; i++){
        var tmp = new Encounters();

        tmp.kareoId = '1234'; //mock.Address.zipCode();

        tmp.patient.fullName = 'Vartan Arabyan'; //mock.Name.findName();
        tmp.patient.dob = Date.now();
        tmp.patient.kareoId = '12312'; //mock.Address.zipCode();

        tmp.physician.fullName = "Dr." + 'Vartan Arabyan'; //mock.Name.findName();
        tmp.physician.kareoId = '12312'; //mock.Address.zipCode();

        tmp.appointmentType = "NCV Upper";
        tmp.appointment = Date.now();

        tmp.save(function(err){
            if (err) throw err;
        });

        if(i == 99999){
            res.send(200, 'Fake Data Loaded');
        }
    }
};

You need to call your tmp variable setting in callbacks. App.VERB() is non-blocking. I'm not familiar with mongoose, however all the code with the tmp variable appears to be blocking. There is no callback, so the variables are being set one by one, becoming noticeable when you are writing this thousands of times.

This loop blocks the event loop until it iterates for 100,000 times. Try this instead:

app.get('/populate/:counter', routes.populate);

exports.populate = function(req, res, next){

     var tmp = new Encounters();

     tmp.kareoId = '1234'; //mock.Address.zipCode();

     tmp.patient.fullName = 'Vartan Arabyan'; //mock.Name.findName();
     tmp.patient.dob = Date.now();
     tmp.patient.kareoId = '12312'; //mock.Address.zipCode();

     tmp.physician.fullName = "Dr." + 'Vartan Arabyan'; //mock.Name.findName();
     tmp.physician.kareoId = '12312'; //mock.Address.zipCode();
     tmp.appointmentType = "NCV Upper";
     tmp.appointment = Date.now();

     tmp.save(function(err){
         if (err) throw err;
     });

     if(req.param('counter') == 99999){
         return res.send(400, 'Fake Data Loaded');
     }
     res.send(200, 'Send me more Data');
};

And then, send 100,000 requests to /populate/:counter route in a loop. You can use another instance of node to create fake requests, you can do something like this:

var http = require('http');

var options = {
    hostname: 'localhost',
    port: 3000,  // I assumed that your express is running on port 3000...
    method: 'GET'
};

var req;
for (var i = 0; i < 100000; i++) {
    options.path = '/populate/' + i;
    req = http.request(options, function(res) {
        res.setEncoding('utf8');
        if (res.statusCode == 400) {
            // Handle 'Fake Data Loaded'
            console.log('Fake data loaded..');
        }
        else 
            console.log('Still not there yet! Send more data...') 
        res.on('data', function (data) {
            console.log(data);
        });
    });

    req.on('error', function(e) {
        console.log('problem with request: ' + e.message);
    });

    req.end();  
};

You have to note that the other node instance will be blocked while making 100,000 http requests. But your express instance will not be blocked while processing these requests...

Rewrite it to be non-blocking and use callbacks

ie. lots of small quick functions rather than one large slow function.

You should try to avoid slow heavy functions. Break it up into small tasks.

The reason it 'hangs' is because node.js is single threaded. I recommend reading up on the node.js Event Loop, callbacks and how to write non-blocking.

If you have lots of small functions, express can respond to requests while the first is 'still running'. Think of the event loop like an array of tasks that are executed in order.

Use the aysnc package .parallel() or .series() - documentation and heaps of examples on their github repo. Use series() if execution order is important and parallel() if not.

In your example, you want to quickly insert 100k rows. Create a function that inserts one row and calls a next() callback function when it's finished. Create a done function that runs your res.send() Use async.times() to execute the create functions then when finished runs the done function.

See async.times() documentation on their github repo:

// Pretend this is some complicated async factory
var createUser = function(id, callback) {
  callback(null, {
    id: 'user' + id
  })
}
// generate 5 users
async.times(5, function(n, next){
    createUser(n, function(err, user) {
      next(err, user)
    })
}, function(err, users) {
  // we should now have 5 users
});