Skip to content

Commit 14f54b0

Browse files
Merge pull request #2743 from timwienk/class-thenable
Class.Thenable
2 parents 22f5714 + aedca57 commit 14f54b0

File tree

15 files changed

+934
-11
lines changed

15 files changed

+934
-11
lines changed

Docs/Class/Class.Extras.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,9 @@ Adds functions to the end of the call stack of the Chain instance.
7474
function(){ this.start(0,1); }
7575
); //Will fade the Element out and in twice.
7676

77+
### Note:
78+
79+
[Take care][resetThenable-note] when using Chain and [Class.Thenable][] together. When multiple "thenable" resolutions are chained, the registration of callbacks for later resolutions has to be chained as well.
7780

7881
### See Also:
7982

@@ -442,6 +445,7 @@ If a Class has [Events][] as well as [Options][] implemented, every option begin
442445

443446
[Class]: /core/Class/Class
444447
[Class:implement]: /core/Class/Class/#Class:implement
448+
[Class.Thenable]: /core/Class/Class/Class.Thenable
445449
[Fx]: /core/Fx/Fx
446450
[Fx.Tween]: /core/Fx/Fx.Tween
447451
[Request]: /core/Request/Request
@@ -451,3 +455,4 @@ If a Class has [Events][] as well as [Options][] implemented, every option begin
451455
[Options]: #Options
452456
[addEvent]: #Events:addEvent
453457
[addEvents]: #Events:addEvents
458+
[resetThenable-note]: /core/Class/Class.Thenable#Class.Thenable:resetThenable-note

Docs/Class/Class.Thenable.md

Lines changed: 227 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,227 @@
1+
Class: Class.Thenable {#Class.Thenable}
2+
=======================================
3+
4+
A Utility Class. Its methods can be implemented with [Class:implement][] into any [Class][].
5+
It makes a Class "thenable" (see [Promises/A+][]), which means you can call its `then` method to integrate it in a [Promise][] style flow.
6+
7+
### Syntax:
8+
9+
#### For new classes:
10+
11+
var MyClass = new Class({ Implements: Class.Thenable });
12+
13+
#### For existing classes:
14+
15+
MyClass.implement(Class.Thenable);
16+
17+
### Implementing:
18+
19+
- This class can be implemented into other classes to add its functionality to them.
20+
21+
### Example:
22+
23+
var Promise = new Class({
24+
Implements: Class.Thenable,
25+
initialize: function(executor){
26+
if (typeof executor !== 'function'){
27+
throw new TypeError('Promise constructor takes a function argument.');
28+
}
29+
30+
try {
31+
executor(this.resolve.bind(this), this.reject.bind(this));
32+
} catch (exception){
33+
this.reject(exception);
34+
}
35+
},
36+
resetThenable: function(){
37+
throw new TypeError('A promise can only be resolved once.');
38+
}
39+
});
40+
41+
var myPromise = new Promise(function(resolve){
42+
resolve('Hello promised world!');
43+
});
44+
myPromise.then(function(value){
45+
console.log(value);
46+
});
47+
48+
### See Also:
49+
50+
- [Class][]
51+
- [Promise][]
52+
53+
54+
Class.Thenable Method: then {#Class.Thenable:then}
55+
--------------------------------------------------
56+
57+
Registers callbacks to receive the class's eventual value or the reason why it cannot be succesfully resolved.
58+
59+
### Syntax:
60+
61+
myClass.then(onFulfilled, onRejected);
62+
63+
### Arguments:
64+
65+
1. onFulfilled - (*function*, optional) Function to execute when the value is succesfully resolved.
66+
2. onRejected - (*function*, optional) Function to execute when the value cannot be succesfully resolved.
67+
68+
### Returns:
69+
70+
* (*object*) A new `Class.Thenable` instance that will have its value resolved based on the return values of the above mentioned arguments, compatible with [Promises/A+][].
71+
72+
### Example:
73+
74+
var request = new Request();
75+
request.send().then(function(response){ console.log(response); });
76+
77+
78+
Class.Thenable Method: catch {#Class.Thenable:catch}
79+
----------------------------------------------------
80+
81+
Registers a callback to receive the reason why an eventual value cannot be succesfully resolved.
82+
83+
### Syntax:
84+
85+
myClass.catch(onRejected);
86+
87+
### Arguments:
88+
89+
1. onRejected - (*function*, optional) Function to execute when the value cannot be succesfully resolved.
90+
91+
### Returns:
92+
93+
* (*object*) A new `Class.Thenable` instance that will have its value resolved based on the return values of the above mentioned arguments, compatible with [Promises/A+][].
94+
95+
### Example:
96+
97+
var request = new Request();
98+
request.send().catch(function(reason){ console.log(reason); });
99+
100+
101+
Class.Thenable Method: resolve {#Class.Thenable:resolve}
102+
--------------------------------------------------------
103+
104+
Function to resolve the eventual value of the `Thenable`.
105+
106+
### Syntax:
107+
108+
myClass.resolve(value);
109+
110+
### Arguments:
111+
112+
1. value - (*mixed*, optional) The value to resolve. If the value is "thenable" (like a [Promise][] or `Thenable`), it will be resolved with its eventual value (i.e. it will be resolved when the "thenable" is resolved).
113+
114+
### Returns:
115+
116+
* (*object*) This Class instance.
117+
118+
### Example:
119+
120+
var MyClass = new Class({
121+
Implements: Class.Thenable,
122+
initialize: function(){
123+
this.resolve('Hello world!');
124+
}
125+
});
126+
127+
128+
Class.Thenable Method: reject {#Class.Thenable:reject}
129+
------------------------------------------------------
130+
131+
Function to make a `Thenable` rejected, that is to say it will not receive an eventual value.
132+
133+
### Syntax:
134+
135+
myClass.reject(reason);
136+
137+
### Arguments:
138+
139+
1. reason - (*mixed*, optional) The reason the `Thenable` will not be succesfully resolved, often an `Error` instance.
140+
141+
### Returns:
142+
143+
* (*object*) This Class instance.
144+
145+
### Example:
146+
147+
var MyClass = new Class({
148+
Implements: Class.Thenable,
149+
initialize: function(){
150+
this.reject(new Error('Cannot be succesfully resolved.'));
151+
}
152+
});
153+
154+
155+
Class.Thenable Method: getThenableState {#Class.Thenable:getThenableState}
156+
--------------------------------------------------------------------------
157+
158+
Returns the state of the `Thenable` Class.
159+
160+
### Syntax:
161+
162+
myClass.getThenableState();
163+
164+
### Returns:
165+
166+
* (*string*) The current state: "pending", "fulfilled" or "rejected".
167+
168+
### Example:
169+
170+
var MyClass = new Class({
171+
Implements: Class.Thenable,
172+
initialize: function(){
173+
console.log(this.getThenableState());
174+
}
175+
});
176+
177+
178+
Class.Thenable Method: resetThenable {#Class.Thenable:resetThenable}
179+
--------------------------------------------------------------------
180+
181+
Resets the state of the `Thenable` Class, to make it usable multiple times. If the current `Thenable` state was not resolved yet, it will be rejected first and the `onRejected` handlers will be executed.
182+
183+
Use with caution, this is not in line with [Promise][] behaviour: a once resolved `Thenable` can be resolved with a different value after it is reset. Useful in case a Class can intentionally receive resolved values multiple times, e.g. a `Request` instance that can be executed multiple times or an `Fx` instance that is used multiple times.
184+
185+
### Syntax:
186+
187+
myClass.resetThenable(reason);
188+
189+
### Arguments:
190+
191+
1. reason - (*mixed*, optional) The reason to pass when rejecting a currently unresolved state.
192+
193+
### Returns:
194+
195+
* (*object*) This Class instance.
196+
197+
### Example:
198+
199+
var MyClass = new Class({
200+
Implements: Class.Thenable,
201+
initialize: function(){
202+
var self = this;
203+
this.addEvent('start', function(){
204+
self.resetThenable();
205+
});
206+
}
207+
});
208+
209+
### Note: {#Class.Thenable:resetThenable-note}
210+
211+
Take care when using [Chain][] and `Class.Thenable` together. When multiple resolutions are chained, the registration of callbacks for later resolutions has to be chained as well. Since `myClass.chain()` returns the class instance, `myClass.chain(fn).then(callback)` is equivalent to `myClass.chain(fn); myClass.then(callback)`, and so the callback is not chained to whatever `fn` does.
212+
213+
To use `chain`, you can use one of the following patterns:
214+
215+
myClass.start().chain(function(){ this.start() }).chain(function(){ this.then(callback); });
216+
myClass.start().chain(function(){ this.start().then(callback); });
217+
218+
If your aim is to use a Promise style flow, you should probably not use `chain`, and just use `then` instead:
219+
220+
myClass.start().then(function(){ return myClass.start(); }).then(callback);
221+
222+
223+
[Chain]: /core/Class/Class.Extras#Chain
224+
[Class]: /core/Class/Class
225+
[Class:implement]: /core/Class/Class/#Class:implement
226+
[Promise]: https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Global_Objects/Promise
227+
[Promises/A+]: https://promisesaplus.com/

Docs/Fx/Fx.md

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ All of the other Fx Classes inherit from this one.
66

77
### Implements:
88

9-
- [Chain][], [Events][], [Options][]
9+
- [Chain][], [Class.Thenable][], [Events][], [Options][]
1010

1111

1212

@@ -30,7 +30,7 @@ Fx Method: constructor {#Fx:constructor}
3030
* link - (*string*: defaults to ignore) Can be 'ignore', 'cancel' and 'chain'.
3131
* 'ignore' - Any calls made to start while the effect is running will be ignored. (Synonymous with 'wait': true from 1.x)
3232
* 'cancel' - Any calls made to start while the effect is running will take precedence over the currently running transition. The new transition will start immediately, canceling the one that is currently running. (Synonymous with 'wait': false from 1.x)
33-
* 'chain' - Any calls made to start while the effect is running will be chained up, and will take place as soon as the current effect has finished, one after another.
33+
* 'chain' - Any calls made to start while the effect is running will be chained up, and will take place as soon as the current effect has finished, one after another. [Take care][resetThenable-note] when using "chain" in combination with Fx's thenable properties.
3434
* duration - (*number*: defaults to 500) The duration of the effect in ms. Can also be one of:
3535
* 'short' - 250ms
3636
* 'normal' - 500ms
@@ -39,6 +39,10 @@ Fx Method: constructor {#Fx:constructor}
3939

4040
transition\[:in\]\[:out\] - for example, 'linear', 'quad:in', 'back:in', 'bounce:out', 'elastic:out', 'sine:in:out'
4141

42+
### Thenable:
43+
44+
Fx implements `Class.Thenable` to make an Fx instance "thenable", i.e. `myFx.start().then(function(){ console.log('Done.'); });`. See [Class.Thenable][] for more information.
45+
4246
### Events:
4347

4448
* start - (*function*) The function to execute when the effect begins.
@@ -202,7 +206,9 @@ Returns true if the animation is paused. You can use this to check if you need t
202206
[Fx.Transitions:sine]: /core/Fx/Fx.Transitions#Fx-Transitions:sine
203207
[Element:setStyle]: /core/Element/Element.Style#Element:setStyle
204208
[Chain]: /core/Class/Class.Extras#Chain
209+
[Class.Thenable]: /core/Class/Class.Thenable
205210
[Events]: /core/Class/Class.Extras#Events
206211
[Options]: /core/Class/Class.Extras#Options
207212
[Fx.Tween]: /core/Fx/Fx.Tween
208213
[Fx.Morph]: /core/Fx/Fx.Morph
214+
[resetThenable-note]: /core/Class/Class.Thenable#Class.Thenable:resetThenable-note

Docs/Request/Request.md

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ An XMLHttpRequest Wrapper.
55

66
### Implements:
77

8-
[Chain][], [Events][], [Options][]
8+
[Chain][], [Class.Thenable][], [Events][], [Options][]
99

1010
### Syntax:
1111

@@ -23,7 +23,7 @@ An XMLHttpRequest Wrapper.
2323
* link - (*string*: defaults to 'ignore') Can be 'ignore', 'cancel' and 'chain'.
2424
* 'ignore' - Any calls made to start while the request is running will be ignored. (Synonymous with 'wait': true from 1.11)
2525
* 'cancel' - Any calls made to start while the request is running will take precedence over the currently running request. The new request will start immediately, canceling the one that is currently running. (Synonymous with 'wait': false from 1.11)
26-
* 'chain' - Any calls made to start while the request is running will be chained up, and will take place as soon as the current request has finished, one after another.
26+
* 'chain' - Any calls made to start while the request is running will be chained up, and will take place as soon as the current request has finished, one after another. [Take care][resetThenable-note] when using "chain" in combination with Request's thenable properties.
2727
* method - (*string*: defaults to 'post') The HTTP method for the request, can be either 'post' or 'get'.
2828
* emulation - (*boolean*: defaults to *true*) If set to true, other methods than 'post' or 'get' are appended as post-data named '\_method' (as used in rails)
2929
* async - (*boolean*: defaults to *true*) If set to false, the requests will be synchronous and freeze the browser during request.
@@ -39,6 +39,10 @@ An XMLHttpRequest Wrapper.
3939
* password - (*string*: defaults to *null*) You can use this option together with the `user` option to set authentication credentials when necessary. Note that the password will be passed as plain text and is therefore readable by anyone through the source code. It is therefore encouraged to use this option carefully
4040
* withCredentials - (*boolean*: defaults to *false*) If set to true, xhr.withCredentials will be set to true allowing cookies/auth to be passed for cross origin requests
4141

42+
### Thenable:
43+
44+
Request implements `Class.Thenable` to make a Request instance "thenable", i.e. `myRequest.send().then(function(response){ console.log(response.text); });`. See [Class.Thenable][] for more information.
45+
4246
### Events:
4347

4448
#### request
@@ -468,7 +472,9 @@ Sends a form or a container of inputs with an HTML request.
468472
[Element.Properties]: /core/Element/Element/#Element-Properties
469473
[URI]: /more/Types/URI
470474
[Chain]: /core/Class/Class.Extras#Chain
475+
[Class.Thenable]: /core/Class/Class.Thenable
471476
[Events]: /core/Class/Class.Extras#Events
472477
[Options]: /core/Class/Class.Extras#Options
473478
[Object:toQueryString]: /core/Types/Object#Object:Object-toQueryString
474479
[Element:toQueryString]: /core/Element/Element#Element:toQueryString
480+
[resetThenable-note]: /core/Class/Class.Thenable#Class.Thenable:resetThenable-note

Gruntfile.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ module.exports = function(grunt){
2626
server: {
2727
name: 'mootools-core-server',
2828
sources: pkg.sources,
29-
components: ['Core/Core', 'Core/Array', 'Core/String', 'Core/Number', 'Core/Function', 'Core/Object', 'Core/Class', 'Core/Class.Extras', 'Core/JSON'],
29+
components: ['Core/Core', 'Core/Array', 'Core/String', 'Core/Number', 'Core/Function', 'Core/Object', 'Core/Class', 'Core/Class.Extras', 'Core/Class.Thenable', 'Core/JSON'],
3030
strip: ['1.2compat', '1.3compat', '1.4compat', '*compat', 'IE', 'ltIE8', 'ltIE9', '!ES5', '!ES5-bind', 'webkit', 'ltFF4'],
3131
specs: ['Specs/Core/*.js', 'Specs/Class/*.js', 'Specs/Types/*.js', 'Specs/Utilities/JSON.js'],
3232
uglify: false

0 commit comments

Comments
 (0)