Send response and continue to perform tasks Express | Node.js

19,994

Solution 1

Am I right that you're trying to execute a CPU-intensive job in performComplexTasks? If so, then event loop is being locked by that task and new requests are waiting until the job is finished.

It's a bad practice in node.js to execute such 'complex' tasks in the same process as http server. Consider using background workers, queues or something like that.

See this topic for details: Node.js and CPU intensive requests

Solution 2

If your job is not super-CPU-intense and you can tolerate some work on the main server thread, then just use await to break the execution so that the request can be properly sent. You can use setTimeout or await.

// This line is wrong too - ask a separate question if needed
var requestTime = Date.now; 

app.post('/messages', async function (req, res) {
  console.log("received request");

  // handle the response
  var body = res.body;
  res.status(200).send({ success: true });
  console.log("sent response");

  // Method 1:
  await performComplexTasks(body)

  // Method 2:
  setTimeout(() => performComplexTasks(body), 0);
})

async function performComplexTasks(body){
   // The line below is required if using the `await` method -
   // it breaks execution and allows your previous function to
   // continue, otherwise we will only resume the other function after
   // this function is completed.
   await new Promise(resolve => setTimeout(resolve, 1));

   // perform data with body data here;
   console.log("finished tasks:", Date.now()-requestTime, "ms");
}

This isn't really a fantastic solution and you'd need to use worker threads for long operations.

Share:
19,994

Related videos on Youtube

No Danger
Author by

No Danger

Updated on June 06, 2022

Comments

  • No Danger
    No Danger almost 2 years

    In Node.js (which I'm new to) I am trying to perform a series of tasks after receiving a response. However, I want to make the response time as fast as possible. I don't need to return the results of these tasks to the client, so I'm trying to return the response immediately.

    My current implementation is roughly:

    var requestTime = Date.now; 
    
    app.post('/messages', function (req, res) {
      console.log("received request");
    
      // handle the response
      var body = res.body;
      res.send('Success');
      res.end();
      console.log("sent response");
    
      performComplexTasks(body)
    })
    
    function performComplexTasks(body){
       // perform data with body data here;
       console.log("finished tasks:", Date.now()-requestTime, "ms");
    }
    
    // -------LOG-----------
    //    received request
    //    POST /api/messages 200 3.685 ms - 59
    //    sent response
    //    finished tasks: 2500ms
    

    The client making the request seems to hang until performComplexTasks() is finished. (The POST finishes in 3.685ms, but the response takes 2500ms to finish.)

    Is there a way to send the response immediately and complete other tasks without having the client wait/hang? (In my case, the client cannot make multiple API calls.)

    • adeneo
      adeneo over 7 years
      What you have should work just fine? As long as the response is ended on the server, what happens after that shouldn't affect the browser.
    • No Danger
      No Danger over 7 years
      @adeneo I've tested this using CURL locally and the response takes between 200- 15000ms. When i comment out performComplexTasks(body) the response takes around 10ms. All other API endpoints (without long tasks) seem to be faster. Could this be because of an independent issue (i.e. with my server CPU usage instead of properly handling req, res)? If so, do you have suggestions on where I should start investigating?
    • No Danger
      No Danger over 7 years
      @adeneo Also, in case it matters, this endpoint will be called by another server (not a browser).
    • adeneo
      adeneo over 7 years
      Try res.status(200).send('Success') and remove res.end, then try wrapping performComplexTasks() in a timeout or nextTick? I have no idea if any of this makes a difference, but it's what I would start with.
  • adeneo
    adeneo over 7 years
    This is true, but the OP's code should end the response, and then do the blocking work so new requests would have to wait etc. it's shouldn't hold up the current request that has already received it's response.
  • teq
    teq over 7 years
    @No Danger What about the very first request to POST /messages when you just started express app? Is it working fast?
  • teq
    teq over 7 years
    @No Danger Are you sure that other endpoints is not affected? Have you tried to call them before and after you called POST /messages? Also make sure that performComplexTasks function doesn't have any "global" variables/locks (shared between requests)
  • colllin
    colllin over 4 years
    If this approach works (will test soon), then even if your task IS cpu-intensive, you could move the cpu-intensive work to a different endpoint (potentially on a different server, or serverless), and send a network request to that endpoint at the end of this handler, which would relieve your main thread of the cpu-intensive work — it would turn it into an easily parallelized async call. You would of course want to make that separate performComplexTasks handler idempotent so that if someone else hits that endpoint it won't break anything.