Lewerow/Zurvan

View on GitHub
detail/ImmediateInterceptor.js

Summary

Maintainability
A
1 hr
Test Coverage
A
100%
'use strict';
var FieldOverrider = require('./utils/FieldOverrider');
var UIDManager = require('./utils/UIDManager');
var SequenceGenerator = require('./utils/SequenceGenerator');
var TypeChecks = require('./utils/TypeChecks');
var assert = require('assert');

function ImmediateInterceptor() {
  this.awaitingImmediates = { size: 0 };
  this.uidManager = new UIDManager();
}

ImmediateInterceptor.prototype.intercept = function(config) {
  this.config = config;

  this.setImmediates = new FieldOverrider(
    global,
    'setImmediate',
    this.addImmediate.bind(this)
  );
  this.clearImmediates = new FieldOverrider(
    global,
    'clearImmediate',
    this.removeImmediate.bind(this)
  );

  if (this.config.fakeNodeDedicatedTimers) {
    var timers = require('timers');
    this.nodeSetImmediates = new FieldOverrider(
      timers,
      'setImmediate',
      setImmediate.bind(global)
    );
    this.nodeClearImmediates = new FieldOverrider(
      timers,
      'clearImmediate',
      setImmediate.bind(global)
    );
  }

  this.enqueue = this.setImmediates.oldValue;
  this.dequeue = this.clearImmediates.oldValue;

  this.uidManager.setUp(this.config.throwOnInvalidClearTimer, 'immediate');

  if (TypeChecks.isFunction(this.config.bluebird)) {
    this.previousBluebirdScheduler = this.config.bluebird.setScheduler(
      this.endOfQueueScheduler()
    );
  } else if (this.config.bluebird !== undefined) {
    throw new Error(
      'if given, bluebird configuration parameter to zurvan must be a function representing bluebird library'
    );
  }
};

ImmediateInterceptor.prototype.release = function(forced) {
  var that = this;

  var leftImmediates = this.awaitingImmediates;
  this.awaitingImmediates = { size: 0 };

  this.setImmediates.restore();
  this.clearImmediates.restore();

  if (this.config.fakeNodeDedicatedTimers) {
    this.nodeSetImmediates.restore();
    this.nodeClearImmediates.restore();
  }

  this.uidManager.clear();

  if (this.previousBluebirdScheduler) {
    this.config.bluebird.setScheduler(this.previousBluebirdScheduler);
    this.previousBluebirdScheduler = undefined;
  }

  this.enqueue = undefined;
  this.dequeue = undefined;

  return leftImmediates;
};

ImmediateInterceptor.prototype.startDroppingImmediates = function() {
  this.dropImmediates = true;
};

ImmediateInterceptor.prototype.addImmediate = function(callback) {
  var uid = this.uidManager.getUid();
  if (this.dropImmediates) {
    this.awaitingImmediates[uid.uid] = new Error().stack;
    ++this.awaitingImmediates.size;
    return uid;
  }

  var that = this;
  var args = [].splice.call(arguments, 1);
  this.awaitingImmediates[uid.uid] = this.enqueue(function() {
    that.remove(uid);
    callback.apply(undefined, args);
  });
  ++this.awaitingImmediates.size;

  return uid;
};

ImmediateInterceptor.prototype.remove = function(uid) {
  var toDequeue;
  if (this.awaitingImmediates[uid.uid] !== undefined) {
    toDequeue = this.awaitingImmediates[uid.uid];
    delete this.awaitingImmediates[uid.uid];
    --this.awaitingImmediates.size;
  }
  assert(this.awaitingImmediates.size >= 0);

  return toDequeue;
};

ImmediateInterceptor.prototype.removeImmediate = function(uid) {
  if (!this.uidManager.isAcceptableUid(uid)) {
    return;
  }

  return this.dequeue(this.remove(uid));
};

ImmediateInterceptor.prototype.areAwaiting = function() {
  return this.awaitingImmediates.size > 0;
};

ImmediateInterceptor.prototype.endOfQueueScheduler = function() {
  return setImmediate.bind(global);
};

ImmediateInterceptor.prototype.internalScheduler = function() {
  return this.enqueue;
};

module.exports = ImmediateInterceptor;