Asychronous IO operation by Node - node.js

I am new to node.js. I am creating a async operation in node with http server module. I want to print hello first and world after 5 seconds on a web page. I have referred this example form Introduction from node js by Ryan Dahl which is 8 years old.
I was wondering whether I have missed anything or something has changed in node.
I have tried with setTimeout and setInterval function but both dosent seem to work.
var http = require('http');
var server = http.createServer((req, res) => {
res.writeHead(200, { 'Content-Type': 'text/plain' })
res.write('Hello \n');
setTimeout(() => {
res.end('World');
},5000)
})
server.listen(8000);
Expected result : Hello prints first followed by world after 5 seconds.
Actual result : Both Hello World prints together after 5 seconds.

Conceptually you are mixing up client and server.
The browser can only render your page when it receives the entire page. (not entirely true*, but you should think of it this way).
If you don't res.end (or res.send with express), the server will hold onto the message(see caveat below). The browser will never render no matter how many .writes you have, because it simply doesn't have the final webpage to render.
Anyway your async code is correct. If you want to see whether the asynchronity is working on the server, use console.logs instead. Whether the .write is actually sent is honestly dependent on many optimization factors you can't see.
*This is actually quite dependent on OS behavior.
https://nodejs.org/api/http.html#http_response_write_chunk_encoding_callback
Observe the API, it may or may not flush the chunk to OS. If its too small, it will usually not be flushed (for performance reasons). The OS then may or may not flush the chunk out (also for performance reasons).
If you want to see this behavior, write a much much much bigger string.
Literally this big:
var server = http.createServer((req, res) => {
res.writeHead(200, { 'Content-Type': 'text/plain' })
res.write('HelloHelloHelloHelloHelloHelloHelloHelloHelloHelloHelloHelloHelloHelloHelloHelloHelloHelloHelloHelloHelloHelloHelloHelloHelloHelloHelloHelloHelloHelloHelloHelloHelloHelloHelloHelloHelloHelloHelloHelloHelloHelloHelloHelloHelloHelloHelloHelloHelloHelloHelloHelloHelloHelloHelloHelloHelloHello');
res.write('HelloHelloHelloHelloHelloHelloHelloHelloHelloHelloHelloHelloHelloHelloHelloHelloHelloHelloHelloHelloHelloHelloHelloHelloHelloHelloHelloHelloHelloHelloHelloHelloHelloHelloHelloHelloHelloHelloHelloHelloHelloHelloHelloHelloHelloHelloHelloHelloHelloHelloHelloHelloHelloHelloHelloHelloHelloHello');
res.write('HelloHelloHelloHelloHelloHelloHelloHelloHelloHelloHelloHelloHelloHelloHelloHelloHelloHelloHelloHelloHelloHelloHelloHelloHelloHelloHelloHelloHelloHelloHelloHelloHelloHelloHelloHelloHelloHelloHelloHelloHelloHelloHelloHelloHelloHelloHelloHelloHelloHelloHelloHelloHelloHelloHelloHelloHelloHello');
res.write('HelloHelloHelloHelloHelloHelloHelloHelloHelloHelloHelloHelloHelloHelloHelloHelloHelloHelloHelloHelloHelloHelloHelloHelloHelloHelloHelloHelloHelloHelloHelloHelloHelloHelloHelloHelloHelloHelloHelloHelloHelloHelloHelloHelloHelloHelloHelloHelloHelloHelloHelloHelloHelloHelloHelloHelloHelloHello');
res.write('HelloHelloHelloHelloHelloHelloHelloHelloHelloHelloHelloHelloHelloHelloHelloHelloHelloHelloHelloHelloHelloHelloHelloHelloHelloHelloHelloHelloHelloHelloHelloHelloHelloHelloHelloHelloHelloHelloHelloHelloHelloHelloHelloHelloHelloHelloHelloHelloHelloHelloHelloHelloHelloHelloHelloHelloHelloHello');
setTimeout(() => {
res.end('World');
},5000)
})

You're getting Hello and world printed on the screen after 5 seconds because the res.end would be the function fired last irrespective of where or how you write it. This is because the res.end function terminates the servers response and only one response can go to the client the way you have configured.
If you truly want to see the output you desire try using console.log instead of res.end and res.write.

Related

How to send sse from a different middleware

it's the first time I've come across SSE, and after a while, i finally got something working. The problem is, that wherever I look, there are always the same examples, of a interval or a loop right inside the original middleware, someting like this:
app.get('/events', async function(req, res) {
console.log('Got /events');
res.set({
'Cache-Control': 'no-cache',
'Content-Type': 'text/event-stream',
'Connection': 'keep-alive'
});
res.flushHeaders();
// Tell the client to retry every 10 seconds if connectivity is lost
res.write('retry: 10000\n\n');
let count = 0;
while (true) {
await new Promise(resolve => setTimeout(resolve, 1000));
console.log('Emit', ++count);
// Emit an SSE that contains the current 'count' as a string
res.write(`data: ${count}\n\n`);
}
});
Now this works, but it's not useful. I am building a simple ordering app. I need to send an sse every time someone orders, so you can see the orders in real time. To do this, I need to send the sse messages after an order has been checked and added to the db. I am not only adding the order in a separate middleware, but even in a separate file, and as a response to a different request. I can't just use res.write, as in the example, because it will affect the response to the /add-order request, and not to the /sse. Is there any way I could do this, possibly even without using sse at all? (I am open to using npm packages as well)
The general idea of SSE remains the same, in your particular case you could use something like the app EventEmitter, when an order is created emit the event, while on the other side subscribe to the event, and each time your receive an event send the data over SSE.

nodeJS prevent timeout on res.download

I have a POST call to my nodeJS server that searches for some data on the mongo database and returns a CSV file with the requested data. The problem is that the data search and processing exceeds the 2 minute nodeJS default timeout.
On a diferent scenario y used:
res.writeHeader(200,'application/json');
res.write('starting fetch .... ');
to keep alive the request and prevent the timeout from the client by sending some res.write(''); from time to time.
Now Im using res.download() to download the resulting csv file, so not just sending JSON as response.
Tried to use this solution like this:
res.writeHeader(200,'application/json');
res.write('starting fetch .... ');
res.download()
But i get the "headers already sent" error.
Any idea on how to prevent the timeout until the data is processed and the file download is done ?
Thanks in advance
A few small things first of all, i think it's supposed to be res.writeHead() instead of res.writeHeader(). Secondly once you send the headers you can not send them again. You are getting the error because res.download() uses res.sendFile() which re-sends the headers. You can't use res.download() or res.sendFile() or res.send() because all of these three methods implicitly set the headers and send as part of the response. You'll need to use a combination of res.writeHead() ,res.write() and res.end(). I am just GET so i can test it from the browser, you can use post obviously.
router.get('/test', function (req,res,next) {
res.writeHead(200, {'Content-Type': 'application/json',
'Content-Disposition': 'attachment',
'filename':'package.json'
});
fs.readFile("package.json",function (err,data) {
if (err) {
throw err;
}else{
setTimeout(function () {
res.write(data);
res.end()
},200000);
}
});
function keepBusy(){
if(!res.finished){
res.writeProcessing();
setTimeout(keepBusy,1000)
}
}
setTimeout(keepBusy,1000);
});
This works fine for me. res.writeProcessing() sends the 102 status code. It is recommended for requests that last greater than 20 second but this blog post says that
102 has been left out more recent revisions of HTTP specifications
but it works fine for me in most browsers. If you have some control over the client as well there can be a much better and reliable solution.

browser doesn't seem to flush data already received from server like cURL does

I'm playing around with node.js sample demo with HTTP Streams (Content-Type: chunked).
var http = require('http');
var server = http.Server(function(req, res) {
res.writeHead(200, {'Content-Type': 'text/plain'});
setInterval(function() {
res.write('world\n');
},2000);
res.write('hello ');
});
server.listen(3000);
Now when I use chrome to view the page, it just times out and I never get to see anything on the screen. Whereas using cURL seems to show me the contents as it is received.
$ curl localhost:3000
hello world
world
world
world
Is this the browser default behavior where it won't show anything unless it has complete data?
Seems like a waste to throw away the data and show a timeout error.
You need to call res.end in order to see the output in the browser.
Try, for example, adding this after res.write('hello'):
setTimeout(function(){res.end();}, 5000);
After five seconds you'll see something like:
Hello world
world
My guess is that cURL shows output right away because it doesn't care about the content type, whereas the browser, which does care, may want more of the response before it shows anything.
You can also try streaming the response by changing the transfer encoding; check out this question for more info on that.

Basic node.js createServer and timed output doesn't work correctly

I'm running Node.js from latest Ubuntu. I got the following code:
var http = require('http');
var s = http.createServer(function(req, res){
res.writeHead(200, {'content-type':'text/html'});
res.write('Hello ');
setTimeout(function(){
res.end('world');
}, 3000);
});
s.listen(8000);
Which I run by executing "node server.js", which is all fine. But the thing is that for some reason, no content is outputted until the setTimeout is called - that's 3 seconds after the request.
I more or less followed the example at http://www.youtube.com/watch?v=jo_B4LTHi3I (see 17:08), but I'm unable to get the same, "streamed" kind of result like he gets (his browser first outputs "Hello" and then "world" 3 seconds later).
I've tried different browsers and even tried to "curl" in Terminal, just like he does on the clip. But it's all the same.
Is some kind of output buffering turned on by default, or why does it act like this?
Thanks in advance!
That's because the browser renders the page only after it receives res.end() which is after 3 seconds. In the case of the implementation shown on the video notice that the line reads
res.write('Hello \n');
Using curl, the line break leads to 'hello' being output to the console and 'world' printed after a lag of 3 seconds.
In both cases i.e. with res.write('Hello') or res.write('Hello \n'), the browser renders it only after receiving res.end(). Moreover if the browser does not receive res.end(), the response will time-out and nothing will be displayed.

Simple Hello World Issue in Node.js

I'm new to node.js and have this very simple code. I just want to say a Hi User every second to the users who have connected to the server.
Here's the code I have:
var http = require('http');
function newfunc(request, response) {
response.writeHead(200, {
"Content-Type": "text/plain",
"connection" : "keep-alive"
});
setInterval(function() {
response.write('Hi User\n');
response.end('');
}, 1000);
}
http.createServer(newfunc).listen(7070);
I see the Hi User message only once, and seems as if setInterval is writing it only once.
What am I doing wrong in this?
EDIT: I stand corrected in the comments... Just remove the response.end() call and you should see something like what you were expecting.
ORIGINAL RESPONSE: What you are trying to do cannot be done in this fashion... The response to an HTTP request is only ever sent once: when response.end() is called (the first time). Commenter points out that this is not really correct: what would be correct to say is that no further data can be sent in the response after end() is called.
If you want to show an HTML page whose contents change every second based on server-side actions, you will need to use something like WebSockets (e.g. the Node-compatible http://socket.io/ library) and some client-side Javascript, which is somewhat more complicated than the code you have above. In general, for non-trivial UI's that do more than just append to the response or do bi-directional communication, this type of approach is preferrable

Resources