Skip to content
Browse files

updated documentation fixed a bug. 0.7.1.

  • Loading branch information...
1 parent 5188ce1 commit e1d8cf3cc62138ea0d504be781b9c51a20dd2e57 @swannodette swannodette committed Nov 16, 2009
Showing with 271 additions and 8 deletions.
  1. +260 −8 Promises.js
  2. +11 −0 readme.textile
View
268 Promises.js
@@ -16,15 +16,28 @@ Function.implement({
/*
Class: Promise
- You can create empty Promise instances, Promises from unsent Request instances
- or from an array of values containing promises.
+ You can create empty Promise instances, Promises from unsent Request
+ instances or from an array of values containing promises.
new Promise();
new Promise(new Request({url:'bar.html'}));
new Promise(["foo", new Promise(new Request({url:'bar.html'})), "baz"]);
- Please note that you should not initialize Promise with a Request which has already
- been sent.
+ Please note that you should not initialize Promise with a Request which
+ has already been sent.
+
+ Options:
+ lazy - The promise is treated as a lazy value. It only trigger a
+ realized event when setValue is called on it. Defaults to false.
+ reduce - A function. Convenience when a promise wraps an array of
+ promises. The function should take an array and return a single value.
+ bare - Useful when using promises to load files. Otherwise a promise
+ expects values from the server to be in the form {"data": ...}. Defaults
+ to false.
+ meta - Store some metadata about the promise. Useful fo debugging.
+ plain - The promise is a plain value. That is it is not a request. Not
+ intended to be used directly. See $lazy.
+ async - Make the promise realize synchronously. Defaults to false.
*/
var Promise = new Class({
@@ -37,19 +50,26 @@ var Promise = new Class({
bare: false,
meta: null,
plain: false,
- async: false
+ async: true
},
initialize: function(value, options) {
this.setOptions(this.defaults, options);
+
this.__realized = false;
this.__ops = [];
this.__plain = this.options.plain;
this.__async = this.options.async;
+
if(this.options.meta) this.setMeta(this.options.meta);
+
+ // if handed a Request object initialize it
if(value && value.xhr) {
this.initReq(value);
} else if(value && $type(value) == "array") {
+ // if handed an array look for promises and watch them.
+ // watch is not lazy, triggers realize, might want to change this
+ // David 11/16/2009
Promise.watch(value, function(promises) {
var values = promises.map(Promise.getValue);
var result = (this.options.reduce && this.options.reduce.apply(null, values)) || values;
@@ -59,23 +79,55 @@ var Promise = new Class({
!Promise.isPromise(value) &&
$type(value) == "object" &&
$H(value).getValues().some(Promise.isPromise)) {
+ // if handed an object look for promises in the values - not recursive
Promise.watch($H(value).getValues(), function(promises) {
this.setValue($H(value).map(Promise.getValue).getClean());
}.bind(this));
} else if(Promise.isPromise(value)) {
+ // if handed a promise, watch it
value.addEvent('realized', function() {
this.setValue(value.value());
}.bind(this))
} else if(typeof value != 'undefined') {
+ // if handed a regular value, set the value immediately but don't
+ // trigger a realized event.
this.__plain = true;
this.setValue(value, false);
}
+
return this;
},
+ /*
+ Function: setMeta
+ *private*
+ Private setter for metadata.
+
+ Parameters:
+ meta - any value.
+ */
setMeta: function(meta) { this.__meta = meta; },
+
+ /*
+ Function: meta
+ Returns the metadata for this promise.
+
+ Returns:
+ A value.
+ */
meta: function() { return this.__meta; },
+ /*
+ Function: initReq
+ *private*
+ Initialize the request. If successfull calls setValue with the value
+ received from the server after applying all operations on the value
+ first. On request failure, setValues to undefined and fires an error
+ event passing itself as data.
+
+ Parameters:
+ req - MooTools Request object.
+ */
initReq: function(req) {
this.__req = req;
req.addEvent('onSuccess', function(responseText) {
@@ -89,25 +141,54 @@ var Promise = new Class({
}.bind(this));
},
+ /*
+ Function: setAsync
+ *private*
+ Private setter for async flag.
+ */
setAsync: function(val) {
this.__async = val;
},
+ /*
+ Function: isAsync
+ *private*
+ Private getter for async flag.
+ */
isAsync: function() {
return this.__async;
},
+ /*
+ Function: realize
+ Realize the value of the promise. If the promise is a request, sends
+ the request. If Promise.debug == true or the async option is set to
+ false the request will be made synchronously. If the value is plain
+ simply calls setValue with the current value of the promise.
+ */
realize: function() {
if(this.__req && !this.__realizing) {
this.__realizing = true;
- if(Promise.debug || this.isAsync()) this.__req.options.async = false;
+ if(Promise.debug || !this.isAsync()) this.__req.options.async = false;
this.__req.send();
} else if(this.__plain) {
this.setValue(this.value());
}
return this;
},
+ /*
+ Function: applyOps
+ *private*
+ Apply any stored operations.
+
+ Parameters:
+ value - a value.
+
+ Returns:
+ The value after each operation has been applied in order to the
+ argument.
+ */
applyOps: function(value) {
var aop = this.__ops.shift();
while(aop) {
@@ -121,8 +202,11 @@ var Promise = new Class({
Method: op
You can modify the value of unrealized Promise with op. If the Promise
is unrealized, the return value will be the Promise itself. If already
- realized, the return value will be value of the Promise after the operation
- has been applied.
+ realized, the return value will be value of the Promise after the
+ operation has been applied.
+
+ Parameters:
+ fn - a function to apply to value of the promise before realization.
*/
op: function(fn) {
if(!this.__realized) {
@@ -134,6 +218,15 @@ var Promise = new Class({
return this.value();
},
+ /*
+ Function: setValue
+ If the promises wraps a Request this is called automatically. If the
+ promise is lazy, calling this will trigger the realized event.
+
+ Parameters:
+ value - a value.
+ notify - whether to trigger the realized event.
+ */
setValue: function(value, notify) {
if(value && value.xhr) {
this.initReq(value);
@@ -146,37 +239,140 @@ var Promise = new Class({
}
},
+ /*
+ Function: value
+ Returns the value of the promise. If called with true as it's argument
+ will apply any queued up operations first.
+
+ Paramters:
+ applyOps - a boolean.
+
+ Returns:
+ The current value of the promise.
+ */
value: function(applyOps) {
if(applyOps !== false) this.__value = this.applyOps(this.__value);
return this.__value;
},
+ /*
+ Function: isRealized
+ Returns whether the promise has been realized yet.
+
+ Returns:
+ A boolean.
+ */
isRealized: function() { return this.__realized; },
+
+ /*
+ Function: isNotRealized
+ Returns whether the promise has been realized yet.
+
+ Returns:
+ A boolean.
+ */
isNotRealized: function() { return !this.__realized; },
+
+ /*
+ Function: isLazy
+ Public getter for whether the promise is being used for storing
+ lazy values.
+
+ Returns:
+ A boolean.
+ */
isLazy: function() { return this.options.lazy; },
+
+ /*
+ Function: isNotLazy
+ Public getter for whether the promise is being used for storing
+ lazy values.
+
+ Returns:
+ A boolean.
+ */
isNotLazy: function() { return !this.options.lazy; },
+ /*
+ Function: get
+ Often you only want a specific field from the JSON data. Get returns
+ promise from promise for a specific field. Uses Function.get from
+ Functools.
+
+ Parameters:
+ Variable
+
+ Returns:
+ A new Promise instance.
+
+ SeeAlso:
+ Function.get
+ */
get: function() {
var args = $A(arguments);
if(!this.isRealized()) return (new Promise(this, {lazy:this.options.lazy})).op(function(v) { return Function.get.apply(null, [v].extend(args)); });
return Function.get.apply(null, [this.value()].extend(args));
},
+ /*
+ Function: fn
+ Similar to op except that the function is applied after the promise is
+ realized. Returns a new promise.
+
+ Parameters:
+ A function to apply to realized value.
+
+ Returns:
+ A new Promise instance.
+ */
fn: function(fn) {
if(!this.isRealized()) return (new Promise(this, {lazy:this.options.lazy})).op(fn);
return fn(this.value());
}
});
+/*
+ Function: $P, $promise
+ Convenience for creating promises.
+*/
var $P = $promise = function(v, options) { return new Promise(v, options); };
+
+/*
+ Function: $lazy
+ Creates Promises which can be uses as lazy values. See readme.textile
+ for more details.
+
+ Returns:
+ A Promise instance with the lazy and plain options set to true.
+*/
var $lazy = function(v, options) { return new Promise(v, $merge({lazy:true, plain:true}, options)); };
+/*
+ Constant: Promise.debug
+ Global flag for running Promises in debug mode. All promises will fetch
+ their remote data synchronously.
+*/
Promise.debug = false;
+/*
+ Function: Promise.iPromise
+ Convenience for determining whether an object is a Promise instance.\n
+
+ Paramters:
+ obj - a value.
+
+ Returns:
+ A boolean.
+*/
Promise.isPromise = function(obj) {
return (obj && obj.name == "Promise");
}
+/*
+ Function: Promise.getValue
+ Get the value of promise. Checks to see if the value is indeed a promise
+ and whether it is realized.
+*/
Promise.getValue = function(v) {
while (Promise.isPromise(v) && v.isRealized()) v = v.value();
return v;
@@ -186,6 +382,12 @@ Promise.getValue = function(v) {
Function: Promise.toValues
Map an array of values to only non-Promise values or
unrealized Promise values.
+
+ Parameters:
+ An array of values.
+
+ Returns:
+ Returns the passed in array.
*/
Promise.toValues = function(ary) {
while(ary.some(Promise.isPromise)) ary = ary.map(Promise.getValue);
@@ -198,6 +400,12 @@ Promise.toValues = function(ary) {
if v is Promise which has been realized returns the promises
value. If it is an unrealized promise or not a promise,
the promise/value is simply returned.
+
+ Parameters:
+ v - a value.
+
+ Returns:
+ A value.
*/
Promise.promiseOrValue = function(v) {
if(v && v.xhr) return new Promise(v);
@@ -221,8 +429,10 @@ Promise.promiseOrValue = function(v) {
Promise.watch = function(args, cb, errCb) {
var promises = args.filter(Promise.isPromise);
var unrealized = promises.filter(Function.msg("isNotRealized"));
+
if(unrealized.length > 0) {
var watching = new Group(unrealized);
+
watching.addEvent('realized', function() {
args = args.map(Promise.getValue);
if(!Promise.allRealized(args)) {
@@ -231,17 +441,31 @@ Promise.watch = function(args, cb, errCb) {
cb(Promise.toValues(args));
}
});
+
if(errCb) {
unrealized.each(function(aPromise) {
aPromise.addEvent('error', errCb.bind(null, [aPromise]));
});
}
+
+ // don't attempt to realize lazy values. They are realized then setValue is called on them.
unrealized.filter(Function.msg('isNotLazy')).each(Function.msg('realize'));
} else {
cb(Promise.toValues(args))
}
}
+/*
+ Function: Promise.allRealized
+ Check whether an array of values contains only values and/or realized
+ Promises.
+
+ Parameters:
+ vs - an array.
+
+ Returns:
+ A boolean.
+*/
Promise.allRealized = function(vs) { return vs.filter(Promise.isPromise).every(Function.msg("isRealized")); }
/*
@@ -250,12 +474,19 @@ Promise.allRealized = function(vs) { return vs.filter(Promise.isPromise).every(F
can handle Promises as arguments. If this new function recieves any Promises
it will continue to block until it can convert all of it's arguments to
non-Promise values.
+
+ Parameters:
+ fn - the function to decorate.
+
+ Returns:
+ A function decorate to handle promises in it's arguments.
*/
function promise(fn) {
return function decorator() {
var args = $A(arguments);
var promises = args.filter(Promise.isPromise);
var unrealized = promises.filter(Function.msg('isNotRealized'));
+
if(unrealized.length > 0) {
if(!Promise.debug) {
var p = new Promise();
@@ -292,6 +523,20 @@ function promise(fn) {
}
}
+/*
+ Function: $if
+ It's important to be able to deal with branching conditions when using
+ Promises. Of course this impossible with asynchronous values, therefore
+ the existance of $if.
+
+ Parameters:
+ test - a value. If truth-y true branch is executed, false-y branch
+ otherwise.
+ trueExpr - a function to be executed if the test is truth-y. This function
+ will passed the value of the promise.
+ falseExpr - a function to be executed if the test is false-y. This
+ function will be passed the value of the promise.
+*/
var $if = function(test, trueExpr, falseExpr) {
if(test) {
if($type(trueExpr) == "function") return trueExpr(test);
@@ -302,6 +547,13 @@ var $if = function(test, trueExpr, falseExpr) {
}
}.asPromise();
+/*
+ Function: $and
+ Meant to be used with $if for the test.
+
+ Parameters:
+ Variable.
+*/
var $and = function() {
var args = $A(arguments);
return args.every($identity);
View
11 readme.textile
@@ -1,3 +1,14 @@
+h1. Installation
+
+Promises relies on the "FuncTools":http://github.com/ShiftSpace/functools library. In order to hack on Promises or use it run the following:
+
+<pre class="console">
+$ git clone git://github.com/ShiftSpace/promises.git
+$ cd promises
+$ git submodule init
+$ git submodule update
+</pre>
+
h1. Rationale
The following is written with MooTools in mind however the Promises

0 comments on commit e1d8cf3

Please sign in to comment.
Something went wrong with that request. Please try again.