What exactly is a Node.js event loop tick?

Remember that while JavaScript is single-threaded, all of node's I/O and calls to native APIs are either asynchronous (using platform-specific mechanisms), or run on a separate thread. (This is all handled through libuv.)

So when there's data available on a socket or a native API function has returned, we need a synchronized way to invoke the JavaScript function that is interested in the particular event that just happened.

It's not safe to just call the JS function from the thread where the native event happened for the same reasons that you'd encounter in a regular multi-threaded application – race conditions, non-atomic memory access, and so forth.

So what we do is place the event on a queue in a thread-safe manner. In oversimplified psuedocode, something like:

lock (queue) {
    queue.push(event);
}

Then, back on the main JavaScript thread (but on the C side of things), we do something like:

while (true) {
    // this is the beginning of a tick

    lock (queue) {
        var tickEvents = copy(queue); // copy the current queue items into thread-local memory
        queue.empty(); // ..and empty out the shared queue
    }

    for (var i = 0; i < tickEvents.length; i++) {
        InvokeJSFunction(tickEvents[i]);
    }

    // this the end of the tick
}

The while (true) (which doesn't actually exist in node's source code; this is purely illustrative) represents the event loop. The inner for invokes the JS function for each event that was on the queue.

This is a tick: the synchronous invocation of zero or more callback functions associated with any external events. Once the queue is emptied out and the last function returns, the tick is over. We go back to the beginning (the next tick) and check for events that were added to the queue from other threads while our JavaScript was running.

What can add things to the queue?

  • process.nextTick
  • setTimeout/setInterval
  • I/O (stuff from fs, net, and so forth)
  • crypto's processor-intensive functions like crypto streams, pbkdf2, and the PRNG (which are actually an example of...)
  • any native modules that use the libuv work queue to make synchronous C/C++ library calls look asynchronous

A simpler answer for those new to JavaScript:

The first thing to understand is that JavaScript is a "single-threaded environment". This refers to JavaScript's behavior of executing your blocks of code one at a time from "the event loop" on a single thread. Below there's a rudimentary implemenation of the event loop taken from Kyle Simpson's book ydkJS and afterwards, an explanation:

// `eventLoop` is an array that acts as a queue (first-in, first-out)
var eventLoop = [ ];
var event;

// keep going "forever"
while (true) {
    // perform a "tick"
    if (eventLoop.length > 0) {
        // get the next event in the queue
        event = eventLoop.shift();

        // now, execute the next event
        try {
            event();
        }
        catch (err) {
            reportError(err);
        }
    }
}

The first while loop simulates the event loop. A tick is the dequeuing of an event from the "event loop queue" and the execution of said event.

Please see the response of 'Josh3796' for a more detailed explanation of what happens in the dequeuing and execution of an event.

Also, I recommend reading Kyle Simpson's book for those who are interested in getting a deep understanding of JavaScript. It's completely free and open-source and can be found at this link: https://github.com/getify/You-Dont-Know-JS

The specific section I referenced can be found here: https://github.com/getify/You-Dont-Know-JS/blob/2nd-ed/sync-async/ch1.md