twisted-deferred
Version:
Deferreds following twisteds style.
300 lines (249 loc) • 12.3 kB
JavaScript
(function() {
var AlreadyCalledError, Deferred, DeferredError, Failure, TimeoutError, assert, logError, passthru, timeout,
__hasProp = Object.prototype.hasOwnProperty,
__extends = function(child, parent) { for (var key in parent) { if (__hasProp.call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor; child.__super__ = parent.prototype; return child; },
__slice = Array.prototype.slice,
__bind = function(fn, me){ return function(){ return fn.apply(me, arguments); }; };
assert = require('assert');
Failure = (function(_super) {
var name;
__extends(Failure, _super);
name = "Failure";
function Failure(msg) {
this.message = msg;
}
return Failure;
})(Error);
AlreadyCalledError = (function(_super) {
var name;
__extends(AlreadyCalledError, _super);
function AlreadyCalledError() {
AlreadyCalledError.__super__.constructor.apply(this, arguments);
}
name = "AlreadyCalledError";
return AlreadyCalledError;
})(Failure);
DeferredError = (function(_super) {
var name;
__extends(DeferredError, _super);
function DeferredError() {
DeferredError.__super__.constructor.apply(this, arguments);
}
name = "DeferredError";
return DeferredError;
})(Failure);
TimeoutError = (function(_super) {
var name;
__extends(TimeoutError, _super);
function TimeoutError() {
TimeoutError.__super__.constructor.apply(this, arguments);
}
name = "TimeoutError";
return TimeoutError;
})(Failure);
logError = function(err) {
console.error(err);
return err;
};
exports.succeed = function(result) {
"Return a Deferred that has already had '.callback(result)' called.\n\nThis is useful when you're writing synchronous code to an\nasynchronous interface: i.e., some code is calling you expecting a\nDeferred result, but you don't actually need to do anything\nasynchronous. Just return defer.succeed(theResult).\n\nSee L{fail} for a version of this function that uses a failing\nDeferred rather than a successful one.\n\n@param result: The result to give to the Deferred's 'callback'\n method.\n\n@rtype: L{Deferred}";
var d;
d = new Deferred();
d.callback(result);
return d;
};
exports.fail = function(result) {
var d;
if (result == null) result = null;
"Return a Deferred that has already had '.errback(result)' called.\n\nSee L{succeed}'s docstring for rationale.\n\n@param result: The same argument that L{Deferred.errback} takes.\n\n@raise NoCurrentExceptionError: If C{result} is C{null} but there is no\n current exception state.\n\n@rtype: L{Deferred}";
d = new Deferred();
d.errback(result);
return d;
};
exports.toDeferred = function() {
var args, d, func;
func = arguments[0], args = 2 <= arguments.length ? __slice.call(arguments, 1) : [];
d = new Deferred();
args.push(function() {
var args, err;
err = arguments[0], args = 2 <= arguments.length ? __slice.call(arguments, 1) : [];
if (err) {
return d.errback(err);
} else {
return d.callback.apply(d, args);
}
});
func.apply(void 0, args);
return d;
};
exports.maybeDeferred = function() {
var args, f, result;
f = arguments[0], args = 2 <= arguments.length ? __slice.call(arguments, 1) : [];
"Invoke a function that may or may not return a deferred.\n\nCall the given function with the given arguments. If the returned\nobject is a C{Deferred}, return it. If the returned object is a C{Failure},\nwrap it with C{fail} and return it. Otherwise, wrap it in C{succeed} and\nreturn it. If an exception is raised, convert it to a C{Failure}, wrap it\nin C{fail}, and then return it.\n\n@type f: Any callable\n@param f: The callable to invoke\n\n@param args: The arguments to pass to C{f}\n@param kw: The keyword arguments to pass to C{f}\n\n@rtype: C{Deferred}\n@return: The result of the function call, wrapped in a C{Deferred} if\nnecessary.";
try {
result = f.apply(null, args);
} catch (ex) {
return exports.fail(ex);
}
if (result instanceof Deferred) {
return result;
} else if (result instanceof Failure) {
return fail(result);
} else {
return exports.succeed(result);
}
return null;
};
timeout = function(deferred) {
return deferred.errback(new TimeoutError("Callback timed out"));
};
passthru = function(arg) {
return arg;
};
Deferred = (function() {
"This is a callback which will be put off until later.\n\nWhy do we want this? Well, in cases where a function in a threaded\nprogram would block until it gets a result, for Twisted it should\nnot block. Instead, it should return a Deferred.\n\nThis can be implemented for protocols that run over the network by\nwriting an asynchronous protocol for twisted.internet. For methods\nthat come from outside packages that are not under our control, we use\nthreads (see for example L{twisted.enterprise.adbapi}).\n\nFor more information about Deferreds, see doc/howto/defer.html or\nU{http://twistedmatrix.com/projects/core/documentation/howto/defer.html}";
function Deferred() {
this._runCallbacks = __bind(this._runCallbacks, this);
this._startRunCallbacks = __bind(this._startRunCallbacks, this);
this._continue = __bind(this._continue, this);
this.pause = __bind(this.pause, this);
this.errback = __bind(this.errback, this);
this.callback = __bind(this.callback, this);
this.chainDeferred = __bind(this.chainDeferred, this);
this.addBoth = __bind(this.addBoth, this);
this.addCallback = __bind(this.addCallback, this);
this.addCallbacks = __bind(this.addCallbacks, this); this.callbacks = [];
this.called = 0;
this.paused = 0;
this.timeoutCall = null;
this._runningCallbacks = false;
}
Deferred.prototype.addCallbacks = function(callback, callbackArgs, errback, errbackArgs) {
var cbs;
if (callbackArgs == null) callbackArgs = null;
if (errback == null) errback = passthru;
if (errbackArgs == null) errbackArgs = null;
"Add a pair of callbacks (success and error) to this Deferred.\n\nThese will be executed when the 'master' callback is run.";
cbs = {
'success': [callback, callbackArgs],
'error': [errback, errbackArgs]
};
this.callbacks.push(cbs);
if (this.called) this._runCallbacks();
return this;
};
Deferred.prototype.addCallback = function() {
var args, callback, callbackArgs;
callback = arguments[0], args = 2 <= arguments.length ? __slice.call(arguments, 1) : [];
"Convenience method for adding just a callback.\n\nSee L{addCallbacks}.";
return this.addCallbacks(callback, callbackArgs = args);
};
Deferred.prototype.addErrback = function() {
var args, errback, errbackArgs;
errback = arguments[0], args = 2 <= arguments.length ? __slice.call(arguments, 1) : [];
"Convenience method for adding just an errback.\n\nSee L{addCallbacks}.";
return this.addCallbacks(passthru, null, errback, errbackArgs = args);
};
Deferred.prototype.addBoth = function() {
var args, callback, callbackArgs, errbackArgs;
callback = arguments[0], args = 2 <= arguments.length ? __slice.call(arguments, 1) : [];
"Convenience method for adding a single callable as both a callback\nand an errback.\n\nSee L{addCallbacks}.";
return this.addCallbacks(callback, callbackArgs = args, callback, errbackArgs = args);
};
Deferred.prototype.chainDeferred = function(d) {
"Chain another Deferred to this Deferred.\n\nThis method adds callbacks to this Deferred to call d's callback or\nerrback, as appropriate. It is merely a shorthand way of performing\nthe following::\n\n @addCallbacks(d.callback, undefined, d.errback, undefined)\n\nWhen you chain a deferred d2 to another deferred d1 with\nd1.chainDeferred(d2), you are making d2 participate in the callback\nchain of d1. Thus any event that fires d1 will also fire d2.\nHowever, the converse is B{not} true; if d2 is fired d1 will not be\naffected."; return this.addCallback(d.callback, void 0, d.errback, void 0);
};
Deferred.prototype.callback = function(result) {
"Run all success callbacks that have been added to this Deferred.\n\nEach callback will have its result passed as the first\nargument to the next; this way, the callbacks act as a\n'processing chain'. Also, if the success-callback returns a Failure\nor raises an Exception, processing will continue on the *error*-\ncallback chain."; return this._startRunCallbacks(result);
};
Deferred.prototype.errback = function(fail) {
var f;
if (fail == null) fail = null;
"Run all error callbacks that have been added to this Deferred.\n\nEach callback will have its result passed as the first argument to the\nnext; this way, the callbacks act as a 'processing chain'. Also, if the\nerror-callback returns a non-Failure or doesn't raise an Exception,\nprocessing will continue on the *success*-callback chain.\n\nIf the argument that's passed to me is not a failure.Failure instance, it\nwill be embedded in one. If no argument is passed, a failure.Failure\ninstance will be created based on the current traceback stack.";
f = fail instanceof Failure ? fail : new Failure(fail);
return this._startRunCallbacks(f);
};
Deferred.prototype.pause = function() {
"Stop processing on a Deferred until L{unpause}() is called."; return this.paused++;
};
Deferred.prototype.unpause = function() {
"Process all callbacks made since L{pause}() was called."; this.paused--;
if (this.paused === 0 && this.called) return this._runCallbacks();
};
Deferred.prototype._continue = function(result) {
this.result = result;
return this.unpause();
};
Deferred.prototype._startRunCallbacks = function(result) {
if (this.called) throw new AlreadyCalledError();
this.called = true;
this.result = result;
if (this.timeoutCall) {
try {
this.timeoutCall.cancel();
} catch (ex) {
throw ex;
}
this.timeoutCall = void 0;
}
return this._runCallbacks();
};
Deferred.prototype._runCallbacks = function() {
var args, callback, caller, key, next_cb, _results;
if (this._runningCallbacks) return;
if (!this.paused) {
_results = [];
while (this.callbacks.length > 0) {
next_cb = this.callbacks.shift();
key = this.result instanceof Failure ? 'error' : 'success';
caller = next_cb[key];
callback = caller[0];
args = caller[1] || [];
try {
this._runningCallbacks = true;
try {
args.splice(0, 0, this.result);
if (callback) this.result = callback.apply(null, args);
} catch (ex) {
console.log(ex);
throw ex;
} finally {
this._runningCallbacks = false;
}
if (this.result instanceof Deferred) {
this.pause();
this.result.addBoth(this._continue.bind(this));
break;
} else {
_results.push(void 0);
}
} catch (ex) {
_results.push(this.result = new Failure(ex));
}
}
return _results;
}
};
return Deferred;
})();
exports.Deferred = Deferred;
exports.DeferredList = function(deferreds) {
var d, deferred, done, i, res;
deferred = new Deferred();
done = deferred.callback.bind(deferred);
res = [];
for (i in deferreds) {
d = deferreds[i];
d.addCallback(function(v) {
res.push([null, v]);
if (deferreds.length === res.length) return done(res);
});
d.addErrback(function(err) {
done = deferred.errback.bind(deferred);
res.push([err, null]);
if (deferreds.length === res.length) return done(res);
});
}
return deferred;
};
}).call(this);