kriskowal/q — pull request #259 by wizardwerdna (2013-04-06 07:11:03+00:00)
Title: Q now crashes Node 0.10.x because of changes to nextTick

Q fails pretty spectacularly under Node 0.10.x when too many promises are returned in .then callbacks. This is because Q calls nextTick from within a nextTick-scheduled function, which is no longer permitted more than a few times.

Prior to 0.10.x, process.nextTick deferrals got highest priority (performed before the event loop) unless process.maxNextTick deferrals were queued, in which case, the system would give the event loop a spin before proceeding. In 0.10.x, this was changed to ASSURE that all nextTicks would be completed before the event lop was run again. Accordingly, "recursive" calls to nextTick from nextTick-scheduled functions can now run up the stack and crash the system.

In the first 3 commits, I demonstrate the problem. Running "npm test" will blow up in node, although Jasmine is copacetic. The last commit "fixes" the problem, preferring setImmeiate to process.nextTick where both functions are available. Running "npm test" confirms that the nextTick stack is no longer implicated. While there are ways to continue to use nextTick using a trampoline and private queue, I think this solution may be faster in practice.

>FASTER PROCESS.NEXTTICK

>In v0.8 (and before), the process.nextTick() function scheduled its callback using a spinner on the event loop. This usually caused the callback to be fired before any other I/O. However, it was not guaranteed.

>As a result, a lot of programs (including some parts of Node's internals) began using process.nextTick as a "do later, but before any actual I/O is performed" interface. Since it usually works that way, it seemed fine.

>However, under load, it's possible for a server to have a lot of I/O scheduled, to the point where the nextTick gets preempted for something else. This led to some odd errors and race conditions, which could not be fixed without changing the semantics of nextTick.

>So, that's what we did. In v0.10, nextTick handlers are run right after each call from C++ into JavaScript. That means that, if your JavaScript code calls process.nextTick, then the callback will fire as soon as the code runs to completion, but before going back to the event loop. The race is over, and all is good.

>However, there are programs out in the wild that use recursive calls to process.nextTick to avoid pre-empting the I/O event loop for long-running jobs. In order to avoid breaking horribly right away, Node will now print a deprecation warning, and ask you to use setImmediate for these kinds of tasks instead.

I credit brian's [promise-per0f-tests](https://github.com/cujojs/promise-perf-tests) with the code, nothing that many libraries suffered the same fate as did Q.

Thanks for all the good work.