]>
Commit | Line | Data |
---|---|---|
6527f429 DM |
1 | /*\r |
2 | Ext.promise.Deferred adapted from:\r | |
3 | [DeftJS](https://github.com/deftjs/deftjs5)\r | |
4 | Copyright (c) 2012-2013 [DeftJS Framework Contributors](http://deftjs.org)\r | |
5 | Open source under the [MIT License](http://en.wikipedia.org/wiki/MIT_License).\r | |
6 | */\r | |
7 | \r | |
8 | /**\r | |
9 | * Deferreds are the mechanism used to create new Promises. A Deferred has a single\r | |
10 | * associated Promise that can be safely returned to external consumers to ensure they do\r | |
11 | * not interfere with the resolution or rejection of the deferred operation.\r | |
12 | *\r | |
13 | * A Deferred is typically used within the body of a function that performs an asynchronous\r | |
14 | * operation. When that operation succeeds, the Deferred should be resolved; if that\r | |
15 | * operation fails, the Deferred should be rejected.\r | |
16 | *\r | |
17 | * Each Deferred has an associated Promise. A Promise delegates `then` calls to its\r | |
18 | * Deferred's `then` method. In this way, access to Deferred operations are divided between\r | |
19 | * producer (Deferred) and consumer (Promise) roles.\r | |
20 | *\r | |
21 | * When a Deferred's `resolve` method is called, it fulfills with the optionally specified\r | |
22 | * value. If `resolve` is called with a then-able (i.e.a Function or Object with a `then`\r | |
23 | * function, such as another Promise) it assimilates the then-able's result; the Deferred\r | |
24 | * provides its own `resolve` and `reject` methods as the onFulfilled or onRejected\r | |
25 | * arguments in a call to that then-able's `then` function. If an error is thrown while\r | |
26 | * calling the then-able's `then` function (prior to any call back to the specified\r | |
27 | * `resolve` or `reject` methods), the Deferred rejects with that error. If a Deferred's\r | |
28 | * `resolve` method is called with its own Promise, it rejects with a TypeError.\r | |
29 | *\r | |
30 | * When a Deferred's `reject` method is called, it rejects with the optionally specified\r | |
31 | * reason.\r | |
32 | *\r | |
33 | * Each time a Deferred's `then` method is called, it captures a pair of optional\r | |
34 | * onFulfilled and onRejected callbacks and returns a Promise of the Deferred's future\r | |
35 | * value as transformed by those callbacks.\r | |
36 | *\r | |
37 | * @private\r | |
38 | * @since 6.0.0\r | |
39 | */\r | |
40 | Ext.define('Ext.promise.Deferred', {\r | |
41 | requires: [\r | |
42 | 'Ext.promise.Consequence'\r | |
43 | ],\r | |
44 | \r | |
45 | /**\r | |
46 | * @property {Ext.promise.Promise} promise Promise of the future value of this Deferred.\r | |
47 | */\r | |
48 | promise: null,\r | |
49 | \r | |
50 | /**\r | |
51 | * @property {Ext.promise.Consequence[]} consequences Pending Consequences chained to this Deferred.\r | |
52 | *\r | |
53 | * @private\r | |
54 | */\r | |
55 | consequences: [],\r | |
56 | \r | |
57 | /**\r | |
58 | * @property {Boolean} completed Indicates whether this Deferred has been completed.\r | |
59 | *\r | |
60 | * @private\r | |
61 | */\r | |
62 | completed: false,\r | |
63 | \r | |
64 | /**\r | |
65 | * @property {String} completeAction The completion action (i.e. 'fulfill' or 'reject').\r | |
66 | *\r | |
67 | * @private\r | |
68 | */\r | |
69 | completionAction: null,\r | |
70 | \r | |
71 | /**\r | |
72 | * @property {Mixed} completionValue The completion value (i.e. resolution value or rejection error).\r | |
73 | *\r | |
74 | * @private\r | |
75 | */\r | |
76 | completionValue: null,\r | |
77 | \r | |
78 | constructor: function() {\r | |
79 | var me = this;\r | |
80 | \r | |
81 | me.promise = new Ext.promise.Promise(me);\r | |
82 | me.consequences = [];\r | |
83 | me.completed = false;\r | |
84 | me.completionAction = null;\r | |
85 | me.completionValue = null;\r | |
86 | },\r | |
87 | \r | |
88 | /**\r | |
89 | * Used to specify onFulfilled and onRejected callbacks that will be\r | |
90 | * notified when the future value becomes available.\r | |
91 | *\r | |
92 | * Those callbacks can subsequently transform the value that was\r | |
93 | * fulfilled or the error that was rejected. Each call to `then`\r | |
94 | * returns a new Promise of that transformed value; i.e., a Promise\r | |
95 | * that is fulfilled with the callback return value or rejected with\r | |
96 | * any error thrown by the callback.\r | |
97 | *\r | |
98 | * @param {Function} [onFulfilled] Callback to execute to transform a fulfillment value.\r | |
99 | * @param {Function} [onRejected] Callback to execute to transform a rejection reason.\r | |
100 | * @param {Function} [onProgress] Callback to execute to transform a progress value.\r | |
101 | *\r | |
102 | * @return Promise that is fulfilled with the callback return value or rejected with\r | |
103 | * any error thrown by the callback.\r | |
104 | */\r | |
105 | then: function(onFulfilled, onRejected, onProgress) {\r | |
106 | var me = this,\r | |
107 | consequence = new Ext.promise.Consequence(onFulfilled, onRejected, onProgress);\r | |
108 | \r | |
109 | if (me.completed) {\r | |
110 | consequence.trigger(me.completionAction, me.completionValue);\r | |
111 | }\r | |
112 | else {\r | |
113 | me.consequences.push(consequence);\r | |
114 | }\r | |
115 | \r | |
116 | return consequence.promise;\r | |
117 | },\r | |
118 | \r | |
119 | /**\r | |
120 | * Resolve this Deferred with the (optional) specified value.\r | |
121 | *\r | |
122 | * If called with a then-able (i.e.a Function or Object with a `then`\r | |
123 | * function, such as another Promise) it assimilates the then-able's\r | |
124 | * result; the Deferred provides its own `resolve` and `reject` methods\r | |
125 | * as the onFulfilled or onRejected arguments in a call to that\r | |
126 | * then-able's `then` function. If an error is thrown while calling\r | |
127 | * the then-able's `then` function (prior to any call back to the\r | |
128 | * specified `resolve` or `reject` methods), the Deferred rejects with\r | |
129 | * that error. If a Deferred's `resolve` method is called with its own\r | |
130 | * Promise, it rejects with a TypeError.\r | |
131 | *\r | |
132 | * Once a Deferred has been fulfilled or rejected, it is considered to be complete\r | |
133 | * and subsequent calls to `resolve` or `reject` are ignored.\r | |
134 | *\r | |
135 | * @param {Mixed} value Value to resolve as either a fulfillment value or rejection\r | |
136 | * reason.\r | |
137 | */\r | |
138 | resolve: function(value) {\r | |
139 | var me = this,\r | |
140 | isHandled, thenFn;\r | |
141 | \r | |
142 | if (me.completed) {\r | |
143 | return;\r | |
144 | }\r | |
145 | \r | |
146 | try {\r | |
147 | if (value === me.promise) {\r | |
148 | throw new TypeError('A Promise cannot be resolved with itself.');\r | |
149 | }\r | |
150 | \r | |
151 | if ((Ext.isObject(value) || Ext.isFunction(value)) &&\r | |
152 | Ext.isFunction(thenFn = value.then)) {\r | |
153 | isHandled = false;\r | |
154 | \r | |
155 | try {\r | |
156 | thenFn.call(value, function(value) {\r | |
157 | if (!isHandled) {\r | |
158 | isHandled = true;\r | |
159 | me.resolve(value);\r | |
160 | }\r | |
161 | }, function(error) {\r | |
162 | if (!isHandled) {\r | |
163 | isHandled = true;\r | |
164 | me.reject(error);\r | |
165 | }\r | |
166 | });\r | |
167 | }\r | |
168 | catch (e) {\r | |
169 | if (!isHandled) {\r | |
170 | me.reject(e);\r | |
171 | }\r | |
172 | }\r | |
173 | }\r | |
174 | else {\r | |
175 | me.complete('fulfill', value);\r | |
176 | }\r | |
177 | }\r | |
178 | catch (e) {\r | |
179 | me.reject(e);\r | |
180 | }\r | |
181 | },\r | |
182 | \r | |
183 | /**\r | |
184 | * Reject this Deferred with the specified reason.\r | |
185 | *\r | |
186 | * Once a Deferred has been rejected, it is considered to be complete\r | |
187 | * and subsequent calls to `resolve` or `reject` are ignored.\r | |
188 | *\r | |
189 | * @param {Error} reason Rejection reason.\r | |
190 | */\r | |
191 | reject: function(reason) {\r | |
192 | if (this.completed) {\r | |
193 | return;\r | |
194 | }\r | |
195 | \r | |
196 | this.complete('reject', reason);\r | |
197 | },\r | |
198 | \r | |
199 | /**\r | |
200 | * Updates progress for this Deferred, if it is still pending, triggering it to\r | |
201 | * execute the `onProgress` callback and propagate the resulting transformed progress\r | |
202 | * value to Deferreds that originate from this Deferred.\r | |
203 | *\r | |
204 | * @param {Mixed} progress The progress value.\r | |
205 | */\r | |
206 | update: function(progress) {\r | |
207 | var consequences = this.consequences,\r | |
208 | consequence, i, len;\r | |
209 | \r | |
210 | if (this.completed) {\r | |
211 | return;\r | |
212 | }\r | |
213 | \r | |
214 | for (i = 0, len = consequences.length; i < len; i++) {\r | |
215 | consequence = consequences[i];\r | |
216 | consequence.update(progress);\r | |
217 | }\r | |
218 | },\r | |
219 | \r | |
220 | /**\r | |
221 | * Complete this Deferred with the specified action and value.\r | |
222 | *\r | |
223 | * @param {String} action Completion action (i.e. 'fufill' or 'reject').\r | |
224 | * @param {Mixed} value Fulfillment value or rejection reason.\r | |
225 | *\r | |
226 | * @private\r | |
227 | */\r | |
228 | complete: function(action, value) {\r | |
229 | var me = this,\r | |
230 | consequences = me.consequences,\r | |
231 | consequence, i, len;\r | |
232 | \r | |
233 | me.completionAction = action;\r | |
234 | me.completionValue = value;\r | |
235 | me.completed = true;\r | |
236 | \r | |
237 | for (i = 0, len = consequences.length; i < len; i++) {\r | |
238 | consequence = consequences[i];\r | |
239 | consequence.trigger(me.completionAction, me.completionValue);\r | |
240 | }\r | |
241 | \r | |
242 | me.consequences = null;\r | |
243 | }\r | |
244 | });\r |