Coburn Coburn - 3 months ago 33
Node.js Question

Node.js readline line event callback guaranteed to finish before next call?

I just fixed a bug where I was reading and rewriting files with

readline
and the lines were being written out of order (it ended up being due to an asynchronous
fs.write()
call).

But one thing I thought was happening was that the
readline
line event was coming in in the correct order, but perhaps my callback function for some of the lines was finishing after another
line
event had been handled.

To demonstrate:

line1 event comes in
line1 event finishes handling
line2 event comes in //Takes a long time to process
line3 event comes in
line3 event finishes handling
line2 event finished handling //And because it was after line3, gets written back after too


The final file output from above would be like:

line1
line3
line2


I didn't see any such guarantee in the docs and my testing seems to point that the above is not possible but I'm not sure. Is the above scenario possible with
readline
?

Answer

NodeJS runs your JavaScript code on a single event loop, what the JavaScript specification calls a job queue. That means that when your code is running responding to line2, it is guaranteed not to be called to respond to line3 while it's still running — if that event occurs while your code is running, the job to call your callback is queued but waits in the job queue until you finish and the event loop can pick up the next job in the queue.

Obviously, this is only true for synchronous code, because asynchronous things (like fs.write) only start a process, they don't wait for it to finish; completions are jobs that are added to the queue. So callbacks from asynchronous calls may well happen after the next event came in.

E.g., consider this code:

stream.on("line", function() {
    // do a lot of synchronous work that may tie up the thread for a while
});

you can be certain that your callback will not be called for line 3 while it's still processing the callback for line 2.

But while processing the callback for line 2:

stream.on("line", function() {
    // Code here IS guaranteed to run before we get called for line 3
    callAnAsyncFunction(function(err, data) {
        // Code here is NOT guaranteed to run before we get called for line 3
    });
    // Code here IS guaranteed to run before we get called for line 3
});
Comments