Comparing version 2.2.0 to 2.3.1
395
future.js
@@ -0,1 +1,2 @@ | ||
/*jshint laxcomma:true node:true es5:true onevar:true */ | ||
(function () { | ||
@@ -7,6 +8,6 @@ "use strict"; | ||
function isFuture(obj) { | ||
return obj instanceof future; | ||
return obj instanceof Future; | ||
} | ||
function futureTimeout(time) { | ||
function FutureTimeoutException(time) { | ||
this.name = "FutureTimeout"; | ||
@@ -16,55 +17,46 @@ this.message = "timeout " + time + "ms"; | ||
// | ||
function privatize(obj, pubs) { | ||
var result = {}; | ||
pubs.forEach(function (pub) { | ||
result[pub] = function () { | ||
obj[pub].apply(obj, arguments); | ||
return result; | ||
}; | ||
}); | ||
return result; | ||
} | ||
function Future(global_context, options) { | ||
if (!isFuture(this)) { | ||
return new Future(global_context, options); | ||
} | ||
function future(global_context, options) { | ||
var everytimers = {}, | ||
onetimers = {}, | ||
index = 0, | ||
deliveries = 0, | ||
time = 0, | ||
fulfilled, | ||
data, | ||
timeout_id, | ||
//asap = false, | ||
asap = true, | ||
passenger, | ||
self = this; | ||
var self = this | ||
; | ||
self._everytimers = {}; | ||
self._onetimers = {}; | ||
self._index = 0; | ||
self._deliveries = 0; | ||
self._time = 0; | ||
//self._asap = false; | ||
self._asap = true; | ||
// TODO change `null` to `this` | ||
global_context = ('undefined' === typeof global_context ? null : global_context); | ||
//self._data; | ||
//self._timeout_id; | ||
options = options || {}; | ||
options.error = options.error || function (err) { | ||
throw err; | ||
}; | ||
self._passenger = null; | ||
self.fulfilled = false; | ||
function resetTimeout() { | ||
if (timeout_id) { | ||
clearTimeout(timeout_id); | ||
timeout_id = undefined; | ||
} | ||
self._global_context = global_context; | ||
if (time > 0) { | ||
timeout_id = setTimeout(function () { | ||
self.deliver(new futureTimeout(time)); | ||
timeout_id = undefined; | ||
}, time); | ||
} | ||
} | ||
// TODO change `null` to `this` | ||
self._global_context = ('undefined' === typeof self._global_context ? null : self._global_context); | ||
self.isFuture = isFuture; | ||
self.setContext = function (context) { | ||
global_context = context; | ||
self._options = options || {}; | ||
self._options.error = self._options.error || function (err) { | ||
throw err; | ||
}; | ||
self.setTimeout = function (new_time) { | ||
time = new_time; | ||
resetTimeout(); | ||
}; | ||
self.errback = function () { | ||
@@ -78,4 +70,2 @@ if (arguments.length < 2) { | ||
self.callback = function () { | ||
@@ -88,79 +78,52 @@ var args = Array.prototype.slice.call(arguments); | ||
self.callbackCount = function() { | ||
return Object.keys(everytimers).length; | ||
self.fulfill = function () { | ||
if (arguments.length) { | ||
self.deliver.apply(self, arguments); | ||
} else { | ||
self.deliver(); | ||
} | ||
self.fulfilled = true; | ||
}; | ||
self.when = function (callback, local_context) { | ||
// this self._index will be the id of the everytimer | ||
self._onetimers[self._index] = true; | ||
self.whenever(callback, local_context); | ||
self.deliveryCount = function() { | ||
return deliveries; | ||
return self; | ||
}; | ||
self.whenever = function (callback, local_context) { | ||
var id = self._index, | ||
everytimer; | ||
if ('function' !== typeof callback) { | ||
self._options.error(new Error("Future().whenever(callback, [context]): callback must be a function.")); | ||
return; | ||
} | ||
self.setAsap = function(new_asap) { | ||
if (undefined === new_asap) { | ||
new_asap = true; | ||
} | ||
if (true !== new_asap && false !== new_asap) { | ||
options.error(new Error("Future.setAsap(asap) accepts literal true or false, not " + new_asap)); | ||
if (self._findCallback(callback, local_context)) { | ||
// TODO log | ||
self._options.error(new Error("Future().everytimers is a strict set. Cannot add already subscribed `callback, [context]`.")); | ||
return; | ||
} | ||
asap = new_asap; | ||
}; | ||
everytimer = self._everytimers[id] = { | ||
id: id, | ||
callback: callback, | ||
context: (null === local_context) ? null : (local_context || self._global_context) | ||
}; | ||
// this will probably never get called and, hence, is not yet well tested | ||
function cleanup() { | ||
var new_everytimers = {}, | ||
new_onetimers = {}; | ||
index = 0; | ||
Object.keys(everytimers).forEach(function (id) { | ||
var newtimer = new_everytimers[index] = everytimers[id]; | ||
if (onetimers[id]) { | ||
new_onetimers[index] = true; | ||
if (self._asap && self._deliveries > 0) { | ||
// doesn't raise deliver count on purpose | ||
everytimer.callback.apply(everytimer.context, self._data); | ||
if (self._onetimers[id]) { | ||
delete self._onetimers[id]; | ||
delete self._everytimers[id]; | ||
} | ||
} | ||
newtimer.id = index; | ||
index += 1; | ||
}); | ||
onetimers = new_onetimers; | ||
everytimers = new_everytimers; | ||
} | ||
function findCallback(callback, context) { | ||
var result; | ||
Object.keys(everytimers).forEach(function (id) { | ||
var everytimer = everytimers[id]; | ||
if (callback === everytimer.callback) { | ||
if (context === everytimer.context || everytimer.context === global_context) { | ||
result = everytimer; | ||
} | ||
} | ||
}); | ||
return result; | ||
} | ||
self.hasCallback = function () { | ||
return !!findCallback.apply(self, arguments); | ||
}; | ||
self.removeCallback = function(callback, context) { | ||
var everytimer = findCallback(callback, context); | ||
if (everytimer) { | ||
delete everytimers[everytimer.id]; | ||
onetimers[everytimer.id] = undefined; | ||
delete onetimers[everytimer.id]; | ||
self._index += 1; | ||
if (self._index >= MAX_INT) { | ||
self._cleanup(); // Works even for long-running processes | ||
} | ||
@@ -171,22 +134,21 @@ | ||
self.deliver = function() { | ||
if (fulfilled) { | ||
options.error(new Error("`Future().fulfill(err, data, ...)` renders future deliveries useless")); | ||
self.deliver = function () { | ||
if (self.fulfilled) { | ||
self._options.error(new Error("`Future().fulfill(err, data, ...)` renders future deliveries useless")); | ||
return; | ||
} | ||
var args = Array.prototype.slice.call(arguments); | ||
data = args; | ||
self._data = args; | ||
deliveries += 1; // Eventually reaches `Infinity`... | ||
self._deliveries += 1; // Eventually reaches `Infinity`... | ||
Object.keys(everytimers).forEach(function (id) { | ||
var everytimer = everytimers[id], | ||
Object.keys(self._everytimers).forEach(function (id) { | ||
var everytimer = self._everytimers[id], | ||
callback = everytimer.callback, | ||
context = everytimer.context; | ||
if (onetimers[id]) { | ||
delete everytimers[id]; | ||
delete onetimers[id]; | ||
if (self._onetimers[id]) { | ||
delete self._everytimers[id]; | ||
delete self._onetimers[id]; | ||
} | ||
@@ -205,83 +167,117 @@ | ||
if (args[0] && "FutureTimeout" !== args[0].name) { | ||
resetTimeout(); | ||
self._resetTimeout(); | ||
} | ||
return self; | ||
}; | ||
} | ||
Future.prototype.setContext = function (context) { | ||
var self = this | ||
; | ||
self._global_context = context; | ||
}; | ||
self.fulfill = function () { | ||
if (arguments.length) { | ||
self.deliver.apply(self, arguments); | ||
} else { | ||
self.deliver(); | ||
} | ||
fulfilled = true; | ||
}; | ||
Future.prototype.setTimeout = function (new_time) { | ||
var self = this | ||
; | ||
self._time = new_time; | ||
self._resetTimeout(); | ||
}; | ||
Future.prototype._resetTimeout = function () { | ||
var self = this | ||
; | ||
self.whenever = function (callback, local_context) { | ||
var id = index, | ||
everytimer; | ||
if (self._timeout_id) { | ||
clearTimeout(self._timeout_id); | ||
self._timeout_id = undefined; | ||
} | ||
if ('function' !== typeof callback) { | ||
options.error(new Error("Future().whenever(callback, [context]): callback must be a function.")); | ||
return; | ||
} | ||
if (self._time > 0) { | ||
self._timeout_id = setTimeout(function () { | ||
self.deliver(new FutureTimeoutException(self._time)); | ||
self._timeout_id = undefined; | ||
}, self._time); | ||
} | ||
}; | ||
if (findCallback(callback, local_context)) { | ||
// TODO log | ||
options.error(new Error("Future().everytimers is a strict set. Cannot add already subscribed `callback, [context]`.")); | ||
return; | ||
} | ||
Future.prototype.callbackCount = function() { | ||
var self = this | ||
; | ||
everytimer = everytimers[id] = { | ||
id: id, | ||
callback: callback, | ||
context: (null === local_context) ? null : (local_context || global_context) | ||
}; | ||
return Object.keys(self._everytimers).length; | ||
}; | ||
if (asap && deliveries > 0) { | ||
// doesn't raise deliver count on purpose | ||
everytimer.callback.apply(everytimer.context, data); | ||
if (onetimers[id]) { | ||
delete onetimers[id]; | ||
delete everytimers[id]; | ||
} | ||
} | ||
Future.prototype.deliveryCount = function() { | ||
var self = this | ||
; | ||
index += 1; | ||
if (index >= MAX_INT) { | ||
cleanup(); // Works even for long-running processes | ||
} | ||
return self._deliveries; | ||
}; | ||
return self; | ||
}; | ||
Future.prototype.setAsap = function(new_asap) { | ||
var self = this | ||
; | ||
if (undefined === new_asap) { | ||
new_asap = true; | ||
} | ||
if (true !== new_asap && false !== new_asap) { | ||
self._options.error(new Error("Future.setAsap(asap) accepts literal true or false, not " + new_asap)); | ||
return; | ||
} | ||
self.when = function (callback, local_context) { | ||
// this index will be the id of the everytimer | ||
onetimers[index] = true; | ||
self.whenever(callback, local_context); | ||
self._asap = new_asap; | ||
}; | ||
return self; | ||
}; | ||
Future.prototype._findCallback = function (callback, context) { | ||
var self = this | ||
, result | ||
; | ||
Object.keys(self._everytimers).forEach(function (id) { | ||
var everytimer = self._everytimers[id] | ||
; | ||
// | ||
function privatize(obj, pubs) { | ||
var result = {}; | ||
pubs.forEach(function (pub) { | ||
result[pub] = function () { | ||
obj[pub].apply(obj, arguments); | ||
return result; | ||
}; | ||
}); | ||
return result; | ||
if (callback === everytimer.callback) { | ||
if (context === everytimer.context || everytimer.context === self._global_context) { | ||
result = everytimer; | ||
} | ||
} | ||
}); | ||
return result; | ||
}; | ||
Future.prototype.hasCallback = function () { | ||
var self = this | ||
; | ||
return !!self._findCallback.apply(self, arguments); | ||
}; | ||
Future.prototype.removeCallback = function(callback, context) { | ||
var self = this | ||
, everytimer = self._findCallback(callback, context) | ||
; | ||
if (everytimer) { | ||
delete self._everytimers[everytimer.id]; | ||
self._onetimers[everytimer.id] = undefined; | ||
delete self._onetimers[everytimer.id]; | ||
} | ||
passenger = privatize(self, [ | ||
return self; | ||
}; | ||
Future.prototype.passable = function () { | ||
var self = this | ||
; | ||
self._passenger = privatize(self, [ | ||
"when", | ||
@@ -291,15 +287,38 @@ "whenever" | ||
self.passable = function () { | ||
return passenger; | ||
}; | ||
return self._passenger; | ||
}; | ||
} | ||
// this will probably never get called and, hence, is not yet well tested | ||
Future.prototype._cleanup = function () { | ||
var self = this | ||
, new_everytimers = {} | ||
, new_onetimers = {} | ||
; | ||
function Future(context, options) { | ||
// TODO use prototype instead of new | ||
return (new future(context, options)); | ||
self._index = 0; | ||
Object.keys(self._everytimers).forEach(function (id) { | ||
var newtimer = new_everytimers[self._index] = self._everytimers[id]; | ||
if (self._onetimers[id]) { | ||
new_onetimers[self._index] = true; | ||
} | ||
newtimer.id = self._index; | ||
self._index += 1; | ||
}); | ||
self._onetimers = new_onetimers; | ||
self._everytimers = new_everytimers; | ||
}; | ||
function create(context, options) { | ||
// TODO use prototype hack instead of new? | ||
return new Future(context, options); | ||
} | ||
Future.prototype.isFuture = isFuture; | ||
Future.isFuture = isFuture; | ||
Future.create = create; | ||
module.exports = Future; | ||
}()); |
{ | ||
"name": "future", | ||
"version": "2.2.0", | ||
"version": "2.3.1", | ||
"description": "The promise / subscribe / deferred module of FuturesJS (Ender.JS and Node.JS)", | ||
@@ -5,0 +5,0 @@ "homepage": "https://github.com/coolaj86/futures", |
@@ -22,3 +22,3 @@ Future | ||
, Future = require('future') | ||
, future = Future(context) | ||
, future = Future.create(context) | ||
, err | ||
@@ -48,2 +48,4 @@ , message = "Hello World!" | ||
Creates a Future (aka Promise, Deferred, Subscription, Callback) object. | ||
**Core** | ||
@@ -83,1 +85,28 @@ | ||
* `hasCallback(callback, context=null)` - Returns `true` if the callback is listening | ||
Example | ||
--- | ||
var context = { "foo": "bar" }, | ||
future = Futures.future(context), | ||
err, | ||
message = "Hello World!"; | ||
future.whenever(function (error, data) { | ||
if (error) { | ||
throw err; | ||
} | ||
console.log(this.foo + " says: " + data); | ||
}); | ||
future.setTimeout(100); | ||
future.deliver(err, message); | ||
Output: | ||
"bar says: Hello World" | ||
FutureTimeout: timeout 100ms | ||
at [object SomeObject]:x:y | ||
... | ||
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
New author
Supply chain riskA new npm collaborator published a version of the package for the first time. New collaborators are usually benign additions to a project, but do indicate a change to the security surface area of a package.
Found 1 instance in 1 package
11072
248
110