6 description: Contains a Utility Class that can be implemented into your own Classes to make them "thenable".
8 license: MIT-style license.
12 provides: [Class.Thenable]
19 var STATE_PENDING = 0,
23 var Thenable = Class.Thenable = new Class({
25 $thenableState: STATE_PENDING,
26 $thenableResult: null,
27 $thenableReactions: [],
29 resolve: function(value){
34 reject: function(reason){
39 getThenableState: function(){
40 switch (this.$thenableState){
52 resetThenable: function(reason){
58 then: function(onFulfilled, onRejected){
59 if (typeof onFulfilled !== 'function') onFulfilled = 'Identity';
60 if (typeof onRejected !== 'function') onRejected = 'Thrower';
62 var thenable = new Thenable();
64 this.$thenableReactions.push({
66 fulfillHandler: onFulfilled,
67 rejectHandler: onRejected
70 if (this.$thenableState !== STATE_PENDING){
77 'catch': function(onRejected){
78 return this.then(null, onRejected);
84 resolve: function(value){
86 if (value instanceof Thenable){
89 thenable = new Thenable();
90 resolve(thenable, value);
94 reject: function(reason){
95 var thenable = new Thenable();
96 reject(thenable, reason);
103 function resolve(thenable, value){
104 if (thenable.$thenableState === STATE_PENDING){
105 if (thenable === value){
106 reject(thenable, new TypeError('Tried to resolve a thenable with itself.'));
107 } else if (value && (typeof value === 'object' || typeof value === 'function')){
109 var then = value.then;
111 reject(thenable, exception);
113 if (typeof then === 'function'){
114 var resolved = false;
122 resolve(thenable, nextValue);
128 reject(thenable, reason);
132 } catch (exception) {
135 reject(thenable, exception);
140 fulfill(thenable, value);
143 fulfill(thenable, value);
148 function fulfill(thenable, value){
149 if (thenable.$thenableState === STATE_PENDING){
150 thenable.$thenableResult = value;
151 thenable.$thenableState = STATE_FULFILLED;
157 function reject(thenable, reason){
158 if (thenable.$thenableState === STATE_PENDING){
159 thenable.$thenableResult = reason;
160 thenable.$thenableState = STATE_REJECTED;
166 function reset(thenable){
167 if (thenable.$thenableState !== STATE_PENDING){
168 thenable.$thenableResult = null;
169 thenable.$thenableState = STATE_PENDING;
173 function react(thenable){
174 var state = thenable.$thenableState,
175 result = thenable.$thenableResult,
176 reactions = thenable.$thenableReactions,
179 if (state === STATE_FULFILLED){
180 thenable.$thenableReactions = [];
181 type = 'fulfillHandler';
182 } else if (state == STATE_REJECTED){
183 thenable.$thenableReactions = [];
184 type = 'rejectHandler';
188 defer(handle.pass([result, reactions, type]));
192 function handle(result, reactions, type){
193 for (var i = 0, l = reactions.length; i < l; ++i){
194 var reaction = reactions[i],
195 handler = reaction[type];
197 if (handler === 'Identity'){
198 resolve(reaction.thenable, result);
199 } else if (handler === 'Thrower'){
200 reject(reaction.thenable, result);
203 resolve(reaction.thenable, handler(result));
204 } catch (exception) {
205 reject(reaction.thenable, exception);
212 if (typeof process !== 'undefined' && typeof process.nextTick === 'function'){
213 defer = process.nextTick;
214 } else if (typeof setImmediate !== 'undefined'){
215 defer = setImmediate;
217 defer = function(fn){