3

Let's assume that we have below code, which has timeout set for 5 seconds.

router.get('/timeout', async (req, res, next) => {
      req.setTimeout(5000, () => {
        res.status(503)
        res.send()
      })
      while (true) {
        console.log("I'm alive")
      }
      res.status(200)
      res.send({msg: 'success'})
})

I know that the last two lines will never be reached, but that's not a point. The problem which I want to solve is that the while loop is still working despite response was sent.

Is there some way to kill such still working tasks?

whoami
  • 73
  • 8
  • 1
    The `while` loop will either run forever or never start. While it's running it would be hogging the entire thread and so `req.timeout` wouldn't change. – VLAZ Feb 07 '19 at 15:49
  • Possible duplicate of [Express.js Response Timeout](https://stackoverflow.com/questions/21708208/express-js-response-timeout) Like this? – George Feb 07 '19 at 15:51

2 Answers2

1

There are two types of long running tasks and cancelling is different for both:

1) Asynchronous tasks:

They may take a while, however they are not using the JavaScript engine, instead the engine is in idle to wait for some external data (database / files / timers whatever). In some cases (timers for example) you can easily discard that external action, also you can trigger it as an event as the engine is not blocked and can handle the cancellation. If the async action cannot be cancelled directly (database read for example) you can wait until it is done and cancel it then:

 class Cancelable {
   constructor() { 
     this.cancelled = false;
     this.handlers = [];
   }

   onCancel(handler) { this.handlers.push(handler); }

   cancel() {
     this.cancelled = true;
     this.handlers.forEach(handler => handler());
   }
}


// inside of the request handler:
const canceller = new Cancelable;

req.setTimeout(5000, () => {
    res.status(503);
    res.send();
    canceller.cancel(); // propagate cancellation
});

// Some long running, async cancellable task
const timer = setTimeout(function() {
  res.send("done");
}, 10000 * Math.random())

// on cancellation just remove the timer
canceller.onCancel(() => clearTimeout(timer));

unCancellableAction(function callback() {
  if(canceller.canceled) return; // exit early if it was cancelled
  res.send("done");
});

2) Synchronous tasks: You cannot cancel synchronous tasks directly as the engine is busy doing the task, and can't handle the cancellation. To make them cancellable you have to use polling, the task has to pause its job, check wether it should cancel, and then either continue or abort. In JS that can be done with generator functions (as they can yield their execution):

function runMax(time, action) {
  const gen = action(), start = Date.now();
  let done, value;
  do {
   ({ done, value } = gen.next());
  } while(!done && Date.now() < start + time)
  return value;
}

// inside the request handler:
runMax(5000, function* () {
  while(true) {
   // ... some jobs
   // yield at a safe position to allow abortion:
   yield;
  }
});
Jonas Wilms
  • 132,000
  • 20
  • 149
  • 151
-1

I think you need to add inside the while loop the new if statement to break the loop.

eg.:

while (!req.timeout) {
    if (!req.timeout) {
        break;
    }
}
poorPixel
  • 1
  • 2