2

I have a route in my express app, which is supposed to do the following:

  • Get some data from outside (OK)
  • Show a HTML page with socket.io listening for messages (OK)
  • Perform some calculations, which take a long time
  • Send a message trough socket.io after each one one completed (OK)
  • When all calculations are completed, show a result page (problem)

So, a simplified version of my code is:

module.exports = function(io) { // This is so I can use socket.io inside the route
  var express = require('express');
  var router = express.Router();

  [... and other required]

  router.post('/', function(req, res, next) {
    res.render('loading'); // This renders the template which holds accepts and renders socket.io messages
    pefromCalculaton();
    sendSocketIOMessage();
    session.data = resultData; // I get the result of all calculations and put that in the session. It's a quick fix, don't judge, I've got no presistancy in my project as for now...
    res.redirect('results'); // And here I'd like to go to the other route, which will display the data, getting it from the session.
  }); 
  return router;
}

Since this doesn't work, I am probably trying to do something really stupid here. But what I actually want to do is:

  • Perform calculations
  • While performing the calculations, update progress using sockets
  • When calculation is done, render a template, showing all the results.
Jonathan Hall
  • 75,165
  • 16
  • 143
  • 189
bitstream
  • 1,108
  • 2
  • 19
  • 30

2 Answers2

4

Well, my friend, as you know that one can't send two responses from within one request. So here is what you need to do.

module.exports = function(io) { // This is so I can use socket.io inside the route
  var express = require('express');
  var router = express.Router();

  [... and other required]

router.post('/', function(req, res, next) {
    var taskId = (new Date()).getTime(); //timestamp to get unique task id.
    res.render('loading');
    startCalculations(function(progressIndicator){  //progress callback
         io.emit('progress',progressIndicator);
    },function(result){  // Finish callback
         session[taskId] = result;
         io.emit('finish',{ taskid: taskId });
    });
});


router.get('/result:taskId', function(req, res, next) {
    var result = session[req.params.taskId];
    if(!result)
    {
       //Result already consumed!
       res.render('expired');
       return;
    }
    delete session[req.params.taskId];
    res.render('result', {result: result});
});

 //progress callback will be called when we want to report progress
 //done callback indicates our results are ready.
function startCalculations(progress, done){
    //This is just a stupid piece of code, just to emulate loading.
    //Your awesome async code will replace this
    //In your case it will not be a loop and there will be a callback
    //To signal finish.
    var result = 0;
    for(i = 0; i < 100; i++)
    {
         result = result + i;
         //Simulate progress
         progress(i);
    }
    //Simulate finish -- result has 0 to 99 all added up ;)
    done(result);

}


return router;
}

Now on the html front you can have ...

this is how your loading view would look like.

<script src="/socket.io/socket.io.js"></script>
<script src="http://code.jquery.com/jquery-1.11.1.js"></script>
<script>
  var socket = io();

 //Init code.

  socket.on('progress', function(progressIndicator){
    //Your jquery mojo to show progress
    displayProgress(progressIndicator);
  });

  socket.on('finish', function(task){
    //Redirect to result page from frontend (you were trying to do
    //on backend -- node.js)
    window.location.href = "/result/" + task.taskId;
    //OR
    //window.location.replace("/result/" + task.taskId);
  });
</script>

Hope this makes sense and helps ...

Let me know if you need anything else.

Have fun!

Dave Amit
  • 2,299
  • 12
  • 17
  • You, Sir, are my hero! Thank you so much for this! – bitstream Mar 02 '16 at 09:15
  • You are most welcome my friend. Keep up the good work and be awesome! – Dave Amit Mar 02 '16 at 09:28
  • There is one problem though. When I don't do a res.send() or something similar after I set the session variable (session[tadkId]), it seems that it's not persisting and I can't get in on the other side. Otherwise it's all working. I saw that othe people have the same problem here: http://stackoverflow.com/questions/13426800/express-session-not-persisting – bitstream Mar 02 '16 at 12:33
  • Ok, fixed it! What I had to do is ad a req.session.save() call after I've set req.session.taskId :) – bitstream Mar 02 '16 at 12:49
-1

Node is asynchronous. Use callbacks or promises to make sure that the result page is shown only when the calculations has been completed.

boomcode
  • 399
  • 1
  • 14