Jez Jez - 2 months ago 14
Node.js Question

Why are my request and response messages out of order?

I'm working through a book that teaches you the basics of Node.JS and I've created a couple of programs - one responder and one requester.

The responder:

"use strict";

const fs = require("fs");
const zmq = require("zmq");
const responder = zmq.socket("rep"); // Create socket to reply to client requests

// Handle incoming requests
responder.on("message", function(data) {
// Parse incoming message
let request = JSON.parse(data);
console.log("Received request to get: " + request.path);

// Read file and reply with content
fs.readFile(request.path, function(err, content) {
console.log("Sending response content");
responder.send(JSON.stringify({
content: content.toString(),
timestamp: Date.now(),
pid: process.pid
}));
});
});

// Listen on TCP port 5433
responder.bind("tcp://127.0.0.1:5433", function(err) {
console.log("Listening for zmq requesters...");
});

// Close the responder when the Node process ends
process.on("SIGINT", function() {
console.log("Shutting down...");
responder.close();
});


The requester:

"use strict";

const zmq = require("zmq");
const filename = process.argv[2];
const requester = zmq.socket("req"); // Create request endpoint

// Handle replies from responder
requester.on("message", function(data) {
let response = JSON.parse(data);
console.log("Received response:", response);
});

requester.connect("tcp://localhost:5433");

// Send request for content
for (let i=1; i <= 3; i++) {
console.log("Sending request " + i + " for " + filename);
requester.send(JSON.stringify({
path: filename
}));
}


So I run the responder program which starts up fine, then I run the requester program like this (
target.txt
already exists on the filesystem):

> node requester.js target.txt


The weird thing is that, given Node.js's single threading, I would expect the output to always be:

Sending request 1 for target.txt
Sending request 2 for target.txt
Sending request 3 for target.txt
Received response: { ...


However, sometimes I get that, but sometimes I'm getting:

Sending request 1 for target.txt
Sending request 2 for target.txt
Received response: { ...
Sending request 3 for target.txt


How can this be? The event loop is executing my
for
loop, which should mean that the "Sending request" lines get output, and then it has the opportunity to invoke the response handler. Why am I sometimes getting a response logged before the third request is logged?

Answer

It's apparent that the behaviour you are seeing can only occur if the callback supplied to on is invoked from within the send implementation.

The zmq module contains native code. It receives the responses in native code and then makes them available to the JavaScript host. Its receipt of responses is not tied the the JavaScript event loop. It appears that within the send implementation, if a response is available, the callback supplied to on is invoked. Presumably, any responses not passed to the callback within send calls are passed on the next turn through the event loop.

Comments