Non-block setTimeout for web server - javascript

I have a web server that upon request makes a phone call, waits 3 seconds and then checks if that phone call is still ongoing. I used setTimeout to integrate this but this blocks all other connections to the web server until the timeout has finished.
// example get request
app.get("/", function(req, res) {
// take an action
example.makeCall(function() {
setTimeout(function() {
// check the action
example.checkCall(function() {
res.status(200)
})
}, 3000)
})
})
Is there some other way of adding a timeout to a request without blocking all other incoming requests?

Not sure why your additional API requests are being blocked, new API requests should use a new invocation of your route's callback function on the call stack and shouldn't be dependant on previous callback functions finishing to be added to the call stack.
The issue might not be that the setTimeout is blocking and may be another problem such as your phone call API blocking new calls being made before a previous call has finished.
But something to try could be to wait 3 seconds in a promise to try to get around potential blocking.
function waitThree() {
return new Promise(function(resolve) {
setTimeout(function() {
resolve();
}, 3000);
});
}
app.get("/", function(req, res) {
// take an action
example.makeCall(function() {
waitThree.then(function() {
example.checkCall(function() {
res.status(200)
});
});
})
});

Related

node.js express: Is there a way to cancel http response in middleware?

I am writing a timeout middleware with express in node.js.
app.use((req, res, next) => {
res.setTimeout(3000, () => {
console.warn("Timeout - response end with 408")
res.status(408).json({ "error": "timeout 408" });
// !!! error will happen with next function when call like `res.send()`:
// Error [ERR_HTTP_HEADERS_SENT]: Cannot set headers after they are sent to the client
next()
})
If there's an endpoint that takes more than 3000 ms, my middleware will repsond with 408. However, the next function will respond again. I don't want to check if the response has been already sent by res.headersSent api every time.
Is there a better way to handle this - like the title said - to cancel the next response in the middleware?
It's your own code in the response handler that is still running (probably waiting for some asynchronous operation to complete). There is no way to tell the interpreter to stop running that code from outside that code. Javascript does not have that feature unless you put that code in a WorkerThread or a separate process (in which case you could kill that thread/process).
If you're just trying to suppress that warning when the code eventually tries to send its response (after the timeout response has already been sent), you could do something like this:
app.use((req, res, next) => {
res.setTimeout(3000, () => {
console.warn("Timeout - response end with 408")
res.status(408).json({ "error": "timeout 408" });
// to avoid warnings after a timeout sent,
// replace the send functions with no-ops
// for the rest of this particular response object's lifetime
res.json = res.send = res.sendFile = res.jsonP = res.end = res.sendStatus = function() {
return this;
}
});
next();
});

Express timeout if endpoint is taking too long

I am trying to timeout a response back to the client if its taking too long on my server. But any solution I have found does not seem to trigger the timeout. This should timeout after 2 seconds, but for some reason my endpoint resolves with no timeout happening. Am I doing this correct?
app.use((req, res, next) => {
res.setTimeout(2000, () => {
console.log('Request has timed out.');
res.status(503).send('Service unavailable. Please retry.');
});
next();
});
// endpoints
...
// resolves after 5 seconds
app.get('/timeoutTest', (req, res) => {
const time = new Date().valueOf();
const futureTime = time + 5000;
while (new Date().valueOf() < futureTime) {
// nothing to do but wait
}
return res.status(200).send('5 seconds have passed since call');
});
res.setTimout does not seem to be triggering if it has the next() at the bottom of the middleware. However. If I comment out next() it does trigger my res.setTimeout after 2 seconds, but of course any http requests coming in wont get reached because there is no next()
There is an express module that will do this already: connect-timeout
Be aware - Node will continue to process your request, so this will not solve CPU usage issues etc.

Stop the execution of a cancelled request in Express

I am creating an application which makes a lot of HTTP requests to another server and it may take up to 1 minute to complete one of such requests. Some of the users cancel the request, however my app still executes the cancelled requests.
Here is my code :
var app = express();
app.get('/something', function (req, res) {
function timeout(i) {
setTimeout(function () {
// lets assume there is a http request.
console.log(i);
timeout(++i);
}, 100);
}
req.connection.on('close', function () {
console.log('I should do something');
});
timeout(1);
});
app.listen(5000);
Basically, What I want is stop console.log(i) calls after client closes connection. Also, If possible the client omit "close-{id}" event and when backend recevies close-{id} event, it terminates {id} request.
Note : I used setTimeout to show callback mechanizim. It is not the real code.
Thank you for your help.
From the documentation, "http.request() returns an instance of the http.ClientRequest class." You could call the abort() method of the returned object. (Warning, untested code).
var http = require('http'); // This needs to go at the top of the file.
app.get('/something', function (req, res) {
var remote_request = http.request("www.something.com", function(data){
res.writeHeader(200, {"Content-type": "text/plain"});
res.end(data);
});
req.on("close", function() {
remote_request.abort();
});
});
Assign your setTimeout to a var, and then use clearTimeout in the close handler. If might take some clever restructuring based on your method structure but something like:
var myTimeout = setTimeout(...)
...
req.connection.on('close', function() {
clearTimeout(myTimeout);
});

Node.js function without callback

I have a node.js server. When a user requests a page I call a function that pulls some info from db and services the request. Simple function with callback then execute response.send
I need to perform secondary computation/database updates which are not necessary for rendering the page request. I don't want the user to wait for these secondary ops to complete (even though they take only 200 ms.)
Is there a way to call a function and exit gracefully without callback?
You can simply do something like this
app.get('/path', function(req, res){
getInfoFromDatabase(); // get info from the database
res.render('myview', {info: data});
// perform post render operations
postRenderingCode();
return;
});
If I understand your problem correctly you can use setTimeout with a value of 0 to place the maintenance code at the end of the execution queue.
function service(user, callback) {
// This will be done later
setTimeout(function() {
console.log("Doing some maintenance work now...");
}, 0);
// Service the user
callback("Here's your data " + user);
}
service("John", function(data) { console.log(data); });
service("Jane", function(data) { console.log(data); });
The output will be:
Here's your data John
Here's your data Jane
Doing some maintenance work now...
Doing some maintenance work now...
You can call your extra ASYNCHRONOUS function before, or after your actual response; for example:
yourCoolFunction() // does awesome stuff...
response.writeHead(200, 'OK');
response.write('some cool data response');
response.end();
Note that the "yourCoolFunction" mentioned must be asynchronous, else the rest of the code will wait for it to complete.
Assuming you're using express.js:
function(req, res, next) {
doSomeAsyncWork(function(e, d) {
// Some logic.
doSomeMoreAsyncWork(function() {})
res.send(/* some data*/)
})
}
Basically you don't really care about the response of the additional async work so you can put in a function that does nothing for the callback.
since I can see none of the answers so far are even somehow helpful, and in order to avoid confusing. What I suggest is use on the object you are working on the following:
function doStuff() {
myObj.emit('myEvent', param);
}
function callback(param) {
do stuff;
}
myObj.on('myEvent', callback);
well, just do what you said, render the page, respond to the request and do whatever you have to do, your code isn't suddenly going to die because you responded to the request.
with express:
function handleTheRequest(req, res) {
res.status(200).send("the response")
// do whatever you like here
}

Abort elasticsearch request using elastic.js

Is there a way to cancel requests/queries to Elasticsearch using elasticjs? The web app I am working on performs a request/query every 5 seconds, but I would like to cancel the request if for some reason the response doesn't show up in 5 seconds (so the browser doesn't pile up a bunch of requests that are unnecessary since the queries are happening repeatedly). I understand this would not prevent Elasticsearch from completing the query, but I would like to at least cancel the request in the browser.
Example:
var request = ejs.Request().doSearch();
var dataFromElasticsearch;
request.then(function (data) {
dataFromElasticsearch = data;
});
setTimeout(function () {
if (!dataFromElasticsearch) {
//do something here to cancel request
}
}, 5000)
Per documentation for elasticsearch.js (3.1, at the time of writing):
...calling the API will return an object (either a promise or just a plain object) which has an abort() method. Calling that abort method ends the HTTP request, but it will not end the work Elasticsearch is doing.
Specifically for your example:
setTimeout(function () {
if (!dataFromElasticsearch) {
request.abort();
}
}, 5000)

Categories

Resources