Permalink
Browse files

Improve predictableNextTick to follow pattern even for nested callbacks

Eg. if you register nextTick inside nextTick
  • Loading branch information...
1 parent dd68fe8 commit c4dfac4059854bdd5811ef0ff9a647a689b15205 @vojtajina committed Jun 30, 2012
Showing with 79 additions and 28 deletions.
  1. +35 −26 lib/util.js
  2. +44 −2 test/util.spec.coffee
View
@@ -4,46 +4,55 @@ var path = require('path');
var nextTickQueue = [];
var nextTickRegistered = false;
+var pointerBase = 0;
+var pointerInPattern = 0;
+
+
+var reset = function() {
+ nextTickRegistered = false;
+ nextTickQueue.length = 0;
+ pointerBase = 0;
+ pointerInPattern = 0;
+ predictableNextTick.pattern = [0];
+};
+
var nextTickHandler = function() {
var pattern = predictableNextTick.pattern;
- var queue = nextTickQueue;
- nextTickRegistered = false;
- nextTickQueue = [];
+ while (pointerBase < nextTickQueue.length) {
+ while (pointerInPattern < pattern.length) {
+ var index = pointerBase + pattern[pointerInPattern];
- var base = 0;
- while (base < queue.length) {
- pattern.forEach(function(i) {
- var index = base + i;
- if (queue[index]) {
+ if (nextTickQueue[index]) {
try {
- queue[index]();
- queue[index] = null;
+ nextTickQueue[index]();
+ nextTickQueue[index] = null;
} catch(e) {
- // filter only fns that still needs to be executed
+ nextTickQueue[index] = null;
+
// just in the case someone will handle the exception
- queue[index] = null;
- var stillNeedToBeExec = queue.filter(function(fn) {
- return fn;
- });
-
- // re-register handler if there are more fns to execute
- if (stillNeedToBeExec.length) {
- nextTickQueue = stillNeedToBeExec.concat(nextTickQueue);
-
- if (!nextTickRegistered) {
- process.nextTick(nextTickHandler);
- nextTickHandlerRegistered = true;
- }
+ if (nextTickQueue.some(function(fn) { return fn; })) {
+ process.nextTick(nextTickHandler);
+ } else {
+ reset();
}
throw e;
}
+ } else {
+ // fill skipped holes, so that predictableNextTick() won't push into these holes
+ while (nextTickQueue.length < index + 1) {
+ nextTickQueue.push(null);
+ }
}
- });
- base += pattern.length;
+ pointerInPattern++;
+ }
+ pointerInPattern = 0;
+ pointerBase += pattern.length;
}
+
+ reset();
};
var predictableNextTick = function(callback) {
View
@@ -19,10 +19,11 @@ describe 'mock-util', ->
it 'should behave predictable based on given pattern', ->
- nextTick.pattern = [1, 0]
stressIt = ->
log = ''
runs ->
+ nextTick.pattern = [1, 0]
+
nextTick -> log += 1
nextTick -> log += 2
nextTick -> log += 3
@@ -63,7 +64,7 @@ describe 'mock-util', ->
# regression
- it 'should survive exception inside callback', ->
+ it 'should survive exception inside callback and fire callbacks registered afterwards', ->
exceptionHandled = false
beforeExceptionSpy = jasmine.createSpy 'before exception'
afterExceptionSpy = jasmine.createSpy 'after exception'
@@ -99,6 +100,47 @@ describe 'mock-util', ->
waitsFor (-> anotherCallback.callCount), 'another later added fn to be called', 100
+ it 'should follow pattern even if callbacks are nested', ->
+ nextTick.pattern = [0, 2, 3, 1]
+ log = []
+
+ nextTick ->
+ log.push '0'
+ nextTick ->
+ log.push '01'
+ nextTick ->
+ log.push '02'
+
+ nextTick ->
+ log.push '1'
+
+ waitsFor (-> log.length is 4), 'all callbacks processed', 100
+ runs ->
+ expect(log).toEqual ['0', '01', '02', '1']
+
+
+ # regression
+ it 'should recover after error', ->
+ spy = jasmine.createSpy 'nextTick callback'
+
+ exceptionHandled = false
+ uncaughtExceptionHandler = (err) ->
+ process.removeListener 'uncaughtException', uncaughtExceptionHandler
+ exceptionHandled = true
+
+ process.on 'uncaughtException', uncaughtExceptionHandler
+
+ nextTick ->
+ throw new Error 'SOME ERR'
+
+ waitsFor (-> exceptionHandled), 'exception handled', 100
+
+ # register another tick callback, after the handled exception
+ runs ->
+ nextTick spy
+ waitsFor (-> spy.callCount), 'spy being called', 100
+
+
#============================================================================
# util.loadFile()
#============================================================================

0 comments on commit c4dfac4

Please sign in to comment.