skip to Main Content

User is lazy, don’t want to wait for data and quit the page. How I can access router.post to stop further code execution?

APP.JS FILE (backend):

var express = require('express');
var expressSession = require('express-session');
var redis = require('redis');
var connectRedis = require('connect-redis');
var body_parser = require('body-parser');
var compression = require('compression');
var RedisStore = connectRedis(expressSession);
var io = require('socket.io');
var data = require('./routes/data.js');
var page = require('./routes/page.js');
var app = express();
app.use(compression());
app.set('view engine', 'ejs');
app.set('views', __dirname + '/pages');
app.use('/', data);
app.use('/page', page);

io.on('connection', function(socket){
    socket.request.session; // session
    socket.id; // id
    soc.on('user-abort', function(id){
        id.specificUser; // we need to close this connection and all the code that is synchronized with this id/socket
    });
});

PAGE.JS FILE (router):

router.post('/page', function(req, res, next)
{
    //we render page so we can send the proper data
    res.render('page');
    req.session // we get id here
    var data = 'data';
    // let suposse we send data to the user partialy:
    function sendData(){
        console.log("data");
        setTimeout(sendData, 1000);
    }
    setTimeout(sendData, 1000);
    // now user quit the page - how I can access router.post to stop the code execution ???
});

module.exports = router;

PAGE.EJS FILE (user side HTML):

window.onbeforeunload = function(e)
{
    socket.emit('abort', {id : specificUser}, function(){
        //user quit the page
    });
};

2

Answers


  1. For all intents and purposes, you can’t, at least not with the code that you’re showing.

    The reason is that by using res.render(), you are causing the connection to the browser to get closed by your server, which means there is no way to detect if the user is quitting the page (well, you could try using window.onunload and sending special "user quit the page" request to your server, but a) that won’t be reliable and b) you’d have to match up that request to the particular timer that was started on behalf of that specific user).

    However, you are suggesting that your code would send additional data to the browser, which it doesn’t (because like I said, res.render() will close the connection when it’s done), so my guess is that your actual code looks differently, in which case it may be possible to detect the user from navigating away from the page.

    EDIT: I would suggest moving the "periodic update" code to the socket.io part of your app. The reason is that HTTP requests are ephemeral, and you’re basically starting a new timer for every request.

    If you’re adamant on running the updates from the request handler, I would probably use a global object to link session id’s (which are known to both the HTTP request handler and the socket.io handler) with socket.io connections, and store timer references and a boolean to reflect if the user has left the page.

    Something like this (very much untested):

    const connections = {}; // this object should accessible in both the socket.io handlers and the HTTP handlers
    
    io.on('connection', function(socket){
      const sid = socket.request.session.id;
    
      // cleanup previous connections
      if (sid in connections) {
        clearTimeout(connections[sid].timer);
        delete connections[sid];
      }
    
      // create new connection
      connections[sid] = {
        timer: null,
        connected: true,
        socket,
      };
    
      socket.on('user-abort', function(id) {
        clearTimeout(connections[sid].timer);
        connections[sid].connected = false;
      });
    });
    
    …
    
    router.post('/page', function(req, res, next) {
      const sid = req.session.id;
    
      res.render('page');
    
      function sendData() {
        if (! connections[sid]?.connected) return;
        connections[sid].socket.emit('hello', 'world');
        connections[sid].timer = setTimeout(sendData, 1000);
      }
      sendData();
    });
    

    But again, I would move sendData to the socket.io handler, I can see a whole lot of issues that may occur using the above code.

    Login or Signup to reply.
  2. If the "further code execution" that you want to prevent is asynchronous (for example, fetches data from a database), you can listen to the close event of the incoming request, if you send the HTML page with res.write, without res.end:

    app.use("/path", function(req, res) {
      var abort;
      res.on("close", function() {
        abort = true;
      });
      function sendData() {
        if (abort)
          console.log("aborted");
        else {
          console.log("<some data>");
          setTimeout(sendData, 1000);
        }
      }
      res.write(`<html>...</html>`);
      sendData();
    });
    
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search