Skip to content

Commit

Permalink
make timeout simpler
Browse files Browse the repository at this point in the history
  • Loading branch information
DonutEspresso committed Jan 24, 2017
1 parent 8534523 commit ed0f508
Show file tree
Hide file tree
Showing 2 changed files with 60 additions and 102 deletions.
78 changes: 19 additions & 59 deletions lib/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -140,33 +140,25 @@ util.inherits(Reissue, events.EventEmitter);
Reissue.prototype._execute = function _execute() {

var self = this;
self._startTime = Date.now();

if (self._active === true) {
// set flag so we know we're currently in user supplied func
self._inUserFunc = true;
// execute their func
self._func.apply(self._funcContext, self._funcArgs);

// if timeout option is specified, schedule one here. basically, we
// execute the next invocation immediately above, then schedule a
// timeout handler, so we basically have two set timeout functions
// being scheduled.
if (self._timeoutMs !== null) {
// assign timeout to self so that we can cancel it if we complete
// on time.
self._timeoutHandlerId = setTimeout(
bind(self._onTimeout, self),
self._timeoutMs
);
// set our own stateful flags on timeout handler
self._timeoutHandlerId.___reissue = {
fired: false,
done: false
};
}
} else {
self._stop();
// start invocation timer
self._startTime = Date.now();
// set flag so we know we're currently in user supplied func
self._inUserFunc = true;
// execute their func
self._func.apply(self._funcContext, self._funcArgs);

// if timeout option is specified, schedule one here. basically, we
// execute the next invocation immediately above, then schedule a
// timeout handler, so we basically have two set timeout functions
// being scheduled.
if (self._timeoutMs !== null) {
// assign timeout to self so that we can cancel it if we complete
// on time.
self._timeoutHandlerId = setTimeout(
bind(self._onTimeout, self),
self._timeoutMs
);
}
};

Expand Down Expand Up @@ -196,16 +188,6 @@ Reissue.prototype._done = function _done(err) {
self.emit('error', err);
}

// if a timeout was setup, and user supplied fn exceeded that timeout and
// we're still waiting for it to complete, block.
if (self._timeoutHandlerId) {
if (self._timeoutHandlerId.___reissue.fired === true &&
self._timeoutHandlerId.___reissue.done === false) {
return self.once('_timeoutComplete', function() {
_internalDone();
});
}
}
// in every other case, we're fine, since we've finished before the
// timeout event has occurred. call _internalDone where we will clear
// the timeout event.
Expand Down Expand Up @@ -286,31 +268,9 @@ Reissue.prototype._onTimeout = function _onTimeout() {
return;
}

self._timeoutHandlerId.___reissue.fired = true;

function _internalOnTimeoutDone() {
self._timeoutHandlerId.___reissue.done = true;
self.emit('_timeoutComplete');
}

// if user is listening to timeout event, fire the event and block on its
// completion.
if (self.listenerCount('timeout') > 0) {
self.emit('timeout', function onTimeoutHandlerComplete(cancel) {
_internalOnTimeoutDone();

// if cancel is true, clear the currently timed out invocation,
// schedule another one immediately.
if (cancel === true) {
clearTimeout(self._nextHandlerId);
self._execute();
}
});
}
// otherwise, we're done
else {
_internalOnTimeoutDone();
}
self.emit('timeout');
};

//------------------------------------------------------------------------------
Expand Down
84 changes: 41 additions & 43 deletions test/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -421,80 +421,78 @@ describe('Reissue module', function() {
interval: 500
});

timer.on('stop', function() {
assert.deepEqual(out, [0,1]);
return done();
});

timer.start();

// this should allow two invocations, calling stop while user supplied
// function is still going on the second time around. once it
// completes, stop should get emitted and the third invocation is never
// scheduled.
setTimeout(function() {
timer.on('stop', function() {
assert.deepEqual(out, [0,1]);
return done();
});
timer.stop();
}, 1000);
});


it('should emit stop, first invocation and timeout should never fire',
function(done) {
it('should emit timeout event', function(done) {

var callCount = 0;
var timeoutFired = false;
var timer = reissue.create({
func: function(callback) {
assert.fail('should not get here!');
return setTimeout(callback, 500);
callCount++;
return setTimeout(callback, 300);
},
interval: 100,
timeout: 200
interval: 1000,
timeout: 150
});

timer.on('timeout', function(callback) {
assert.fail('should not get here!');
return callback();
});
timer.start();

timer.on('stop', done);
timer.on('timeout', function() {
timeoutFired = true;
});

timer.start();
timer.stop();
// first invocation should fire, take too long, and timeout event
// should fire. it should complete before next interval, and while we're waiting for it to
// complete (250ms) stop is called. timeout event should not fire.
setTimeout(function() {
timer.on('stop', function() {
assert.isTrue(timeoutFired);
assert.equal(callCount, 1);
return done();
});
timer.stop();
}, 400);
});


it('should cancel next invocation via timeout callback', function(done) {
it('should stop during invocation, and timeout event should not fire',
function(done) {

var timer = reissue.create({
func: function(callback) {
return setTimeout(callback, 500);
return setTimeout(callback, 250);
},
interval: 100,
timeout: 200
interval: 200,
timeout: 400
});
timer.start();

var start = Date.now();

timer.once('timeout', function(callback) {
timer.once('timeout', function(cb) {
// since we've thrown the first one away, and invoked it again
// immediately, we should be somewhere between 400-500ms
// elapsed
var elapsed = Date.now() - start;
assert.isAtLeast(elapsed, 400);
assert.isAtMost(elapsed, 500);
timer.stop();
return cb();
});

// true cancels the timed out invocation and schedules one
// immediately
return callback(true);
timer.on('timeout', function(callback) {
assert.fail('should not get here!');
return callback();
});

timer.on('stop', function() {
return done();
});
timer.on('stop', done);

timer.start();
// first invocation should fire, and while we're waiting for it to
// complete (250ms) stop is called. timeout event should not fire.
setTimeout(function() {
timer.stop();
}, 100);
});
});

0 comments on commit ed0f508

Please sign in to comment.