javascriptnode.jsconcurrencyeventqueue

A Non Blocking Event Loop in Nodejs


I was trying to implement an event loop. The purpose of the loop is simple:

  1. If, the queue is not empty, let task be the oldest entry in the queue.
  2. Process task
  3. If, task generates a return value, cache it.
  4. Check the queue for task.
  5. If, the queue is not empty, adopt the next task as the current task context.
  6. Pass the cached return value to the new task.

I tried an implementation in Nodejs in the following manner:

function task(){
this.task = Array.prototype.shift.call(arguments);
this.state = 'unprocessed';
this.successCallback = null;
this.failureCallback = null;
}
task.prototype.afterThat = function(){
if(arguments.length > 1){
this.successCallback = Array.prototype.shift.call(arguments);
this.failureCallback = Array.prototype.shift.call(arguments);
}
}
task.prototype._loop = function(){
let ctx = this;
while(ctx.state === 'unprocessed'){ // Source of problem
    setImmediate(this.task, function(){
    // change ctx.state if needed
   }, function(){
   // change ctx.state to 'processed'
   });
 }
}

The differences between this implementation and that of an event loop are as follows:

  1. Rather than maintaining an array of task, the successCallback can instantiate a new task as its return which, is then adopted as the current task.
  2. Based on the processing, the task state must change (or not, if, a new pending task is adopted which, has its state as 'unprocessed').

I believe the problem is the setImmediate invocation which, cannot change the context state because, the current execution state never terminates and keeps adding a new call for the same task in the event queue.

I hope I have explained it well and would appreciate some guidelines for implementation and, any errors that I may have in my understanding of the event queue.

Thanks.


Solution

  • Your while loop is an infinite loop. JS is single threaded and thus nothing else can run until your while loop finishes. Because nothing else can run, ctx.state will never change. Because ctx.state can never change, your while loop runs forever.

    Each time the loop runs it calls setImmediate() which schedules a task to run, but that task can't actually run until the while loop finishes. Thus you're deadlocked and you will be piling up a massive list of tasks to run (eventually overflowing some system resource probably).

    You need to check ctx.state after you complete running a task (probably in the success and failure callbacks), not in a loop like this.

    setImmediate() is not blocking. The function you schedule with setImmediate() does not run until your while loop is done, but that while loop never finds its condition so it never stops because nothing else can run until the while loop is done.

    Instead, call the next task and when that is complete, then check ctx.state. Since you don't show or describe how this is used or what you're really trying to accomplish, I'm unsure the best implementation to suggest. If you want more help with that, then you need to show us a lot more about what you're trying to accomplish and how you want to use this class.

    My guess is that you could use promises to accomplish what you're trying to do without having to write much infrastructure code yourself at all since promises are very good tools for sequencing asynchronous tasks.


    FYI, there are several things wrong with your code, such as missing closing braces and parens. What you have posted will not even parse correctly, much less run. And, please indent code properly when posting.