How to send response asynchronously in nodejs / express - javascript

const express = require("express");
const router = express.Router();
router.get("/", async (req, res, next) => {
setTimeout(() => {
res.send("Hello Stackoverflow");
}, 10000);
});
module.exports = router;
When i send a get request from two browser tabs at same time first request gets completed in 10 seconds and second one takes 20 seconds.
I expected both the requests to get completed in 10 seconds as i used setTimeout to make it async.
Am i doing something wrong here or nodejs does't take another request before completing the first one ?
What i want this code to do is :
request ──> setTimeout
request ──> setTimeout
setTimeout completes ──> send response
setTimeout completes ──> send response
what it currently doing is :
request ──> setTimeout
setTimeout completes ──> send response
request ──> setTimeout
setTimeout completes ──> send response
How can i achieve this ?
EDIT =>
I suspect this is some chrome only behaviour as the code works in firefox. Also if i open one normal tab and another incognito tab it works as expected.

This is caused by your browser wanting to reuse the connection to the Express server for multiple requests because by default, Express (actually, Node.js's http server does this) is telling it to by setting the header Connection: keep-alive (see below for more information).
To be able to reuse the connection, it'll have to wait for a request to finish before it can send another request over the same connection.
In your case, the first request takes 10 seconds, and only then is the second request sent.
To illustrate this behaviour, try this:
router.get("/", async (req, res, next) => {
res.set('connection', 'close');
setTimeout(() => {
res.send("Hello Stackoverflow");
}, 10000);
});
This tells the browser that the connection should be closed after each request, which means that it will create a new connection for the second request instead of waiting for the first request to finish.
For more information, look here: https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Connection

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.

How can I make an asynchronous request (non-blocking) using a Cloudflare Worker

I'm writing a Cloudflare Worker that needs to ping an analytics service after my original request has completed. I don't want it to block the original request, as I don't want latency or a failure of the analytics system to slow down or break requests. How can I create a request which starts and ends after the original request completes?
addEventListener('fetch', event => {
event.respondWith(handle(event))
})
async function handle(event) {
const response = await fetch(event.request)
// Send async analytics request.
let promise = fetch("https://example.com")
.then(response => {
console.log("analytics sent!")
})
// If I uncomment this, only then do I see the
// "analytics sent!" log message. But I don't
// want to wait for it!
// await promise;
return response
}
You need to use Event.waitUntil() to extend the duration of the request. By default, all asynchronous tasks are canceled as soon as the final response is sent, but you can use waitUntil() to extend the request processing lifetime to accommodate asynchronous tasks. The input to waitUntil() must be a Promise which resolves when the task is done.
So, instead of:
await promise
do:
event.waitUntil(promise)
Here's the full working script in the Playground.

Non-block setTimeout for web server

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)
});
});
})
});

How to limit the count of the parallel http requests in node js?

I have a script which I start a several http requests inside a loop
Let's say that I have to make 1000 http requests.
The thing is that I can do only one http request per IP and I have only 10 IPs.
So, after 10 parallel requests, I have to wait a response to make another one.
How do I wait without block the script one response from a http request to start another one?
My problem is if I execute a while waiting for a free IP my whole script is blocked and I do not receive any response.
Use the async module for this.
You can use async#eachLimit to limit the concurrent requests to 10.
var urls = [
// a list of 100 urls
];
function makeRequest(url, callback) {
/* make a http request */
callback(); // when done, callback
}
async.eachLimit(urls, 10, makeRequest, function(err) {
if(err) throw err;
});
This code will loop through the list of urls and call makeRequest for each one. It will stop at 10 concurrent requests and will not proceed with the 11th request until one of the first 10 have finished.

Categories

Resources