/ext-4.1.0_b3/src/data/Batch.js
JavaScript | 236 lines | 75 code | 33 blank | 128 comment | 7 complexity | 34ad13571010d8b6a303e8e6e266f245 MD5 | raw file
1/**
2 * @author Ed Spencer
3 * @class Ext.data.Batch
4 *
5 * <p>Provides a mechanism to run one or more {@link Ext.data.Operation operations} in a given order. Fires the 'operationcomplete' event
6 * after the completion of each Operation, and the 'complete' event when all Operations have been successfully executed. Fires an 'exception'
7 * event if any of the Operations encounter an exception.</p>
8 *
9 * <p>Usually these are only used internally by {@link Ext.data.proxy.Proxy} classes</p>
10 *
11 */
12Ext.define('Ext.data.Batch', {
13 mixins: {
14 observable: 'Ext.util.Observable'
15 },
16
17 /**
18 * @cfg {Boolean} autoStart
19 * True to immediately start processing the batch as soon as it is constructed (defaults to false)
20 */
21 autoStart: false,
22
23 /**
24 * @cfg {Boolean} pauseOnException
25 * True to pause the execution of the batch if any operation encounters an exception
26 * (defaults to false). If you set this to true you are responsible for implementing the appropriate
27 * handling logic and restarting or discarding the batch as needed. There are different ways you could
28 * do this, e.g. by handling the batch's {@link #exception} event directly, or perhaps by overriding
29 * {@link Ext.data.AbstractStore#onBatchException onBatchException} at the store level. If you do pause
30 * and attempt to handle the exception you can call {@link #retry} to process the same operation again.
31 *
32 * Note that {@link Ext.data.Operation operations} are atomic, so any operations that may have succeeded
33 * prior to an exception (and up until pausing the batch) will be finalized at the server level and will
34 * not be automatically reversible. Any transactional / rollback behavior that might be desired would have
35 * to be implemented at the application level. Pausing on exception will likely be most beneficial when
36 * used in coordination with such a scheme, where an exception might actually affect subsequent operations
37 * in the same batch and so should be handled before continuing with the next operation.
38 *
39 * If you have not implemented transactional operation handling then this option should typically be left
40 * to the default of false (e.g. process as many operations as possible, and handle any exceptions
41 * asynchronously without holding up the rest of the batch).
42 */
43 pauseOnException: false,
44
45 /**
46 * @property {Number} current
47 * The index of the current operation being executed. Read only
48 */
49 current: -1,
50
51 /**
52 * @property {Number} total
53 * The total number of operations in this batch. Read only
54 */
55 total: 0,
56
57 /**
58 * @property {Boolean} isRunning
59 * True if the batch is currently running. Read only
60 */
61 isRunning: false,
62
63 /**
64 * @property {Boolean} isComplete
65 * True if this batch has been executed completely. Read only
66 */
67 isComplete: false,
68
69 /**
70 * @property {Boolean} hasException
71 * True if this batch has encountered an exception. This is cleared at the start of each operation. Read only
72 */
73 hasException: false,
74
75 /**
76 * Creates new Batch object.
77 * @param {Object} [config] Config object
78 */
79 constructor: function(config) {
80 var me = this;
81
82 /**
83 * @event complete
84 * Fired when all operations of this batch have been completed
85 * @param {Ext.data.Batch} batch The batch object
86 * @param {Object} operation The last operation that was executed
87 */
88
89 /**
90 * @event exception
91 * Fired when a operation encountered an exception
92 * @param {Ext.data.Batch} batch The batch object
93 * @param {Object} operation The operation that encountered the exception
94 */
95
96 /**
97 * @event operationcomplete
98 * Fired when each operation of the batch completes
99 * @param {Ext.data.Batch} batch The batch object
100 * @param {Object} operation The operation that just completed
101 */
102
103 me.mixins.observable.constructor.call(me, config);
104
105 /**
106 * Ordered array of operations that will be executed by this batch
107 * @property {Ext.data.Operation[]} operations
108 */
109 me.operations = [];
110
111 /**
112 * Ordered array of operations that raised an exception during the most recent
113 * batch execution and did not successfully complete
114 * @property {Ext.data.Operation[]} exceptions
115 */
116 me.exceptions = [];
117 },
118
119 /**
120 * Adds a new operation to this batch at the end of the {@link #operations} array
121 * @param {Object} operation The {@link Ext.data.Operation Operation} object
122 * @return {Ext.data.Batch} this
123 */
124 add: function(operation) {
125 this.total++;
126
127 operation.setBatch(this);
128
129 this.operations.push(operation);
130
131 return this;
132 },
133
134 /**
135 * Kicks off execution of the batch, continuing from the next operation if the previous
136 * operation encountered an exception, or if execution was paused. Use this method to start
137 * the batch for the first time or to restart a paused batch by skipping the current
138 * unsuccessful operation.
139 *
140 * To retry processing the current operation before continuing to the rest of the batch (e.g.
141 * because you explicitly handled the operation's exception), call {@link #retry} instead.
142 *
143 * Note that if the batch is already running any call to start will be ignored.
144 *
145 * @return {Ext.data.Batch} this
146 */
147 start: function(/* private */ index) {
148 var me = this;
149
150 if (me.isRunning) {
151 return me;
152 }
153
154 me.exceptions.length = 0;
155 me.hasException = false;
156 me.isRunning = true;
157
158 return me.runOperation(Ext.isDefined(index) ? index : me.current + 1);
159 },
160
161 /**
162 * Kicks off execution of the batch, continuing from the current operation. This is intended
163 * for restarting a {@link #pause paused} batch after an exception, and the operation that raised
164 * the exception will now be retried. The batch will then continue with its normal processing until
165 * all operations are complete or another exception is encountered.
166 *
167 * Note that if the batch is already running any call to retry will be ignored.
168 *
169 * @return {Ext.data.Batch} this
170 */
171 retry: function() {
172 return this.start(this.current);
173 },
174
175 /**
176 * @private
177 * Runs the next operation, relative to this.current.
178 * @return {Ext.data.Batch} this
179 */
180 runNextOperation: function() {
181 return this.runOperation(this.current + 1);
182 },
183
184 /**
185 * Pauses execution of the batch, but does not cancel the current operation
186 * @return {Ext.data.Batch} this
187 */
188 pause: function() {
189 this.isRunning = false;
190 return this;
191 },
192
193 /**
194 * Executes an operation by its numeric index in the {@link #operations} array
195 * @param {Number} index The operation index to run
196 * @return {Ext.data.Batch} this
197 */
198 runOperation: function(index) {
199 var me = this,
200 operations = me.operations,
201 operation = operations[index],
202 onProxyReturn;
203
204 if (operation === undefined) {
205 me.isRunning = false;
206 me.isComplete = true;
207 me.fireEvent('complete', me, operations[operations.length - 1]);
208 } else {
209 me.current = index;
210
211 onProxyReturn = function(operation) {
212 var hasException = operation.hasException();
213
214 if (hasException) {
215 me.hasException = true;
216 me.exceptions.push(operation);
217 me.fireEvent('exception', me, operation);
218 }
219
220 if (hasException && me.pauseOnException) {
221 me.pause();
222 } else {
223 operation.setCompleted();
224 me.fireEvent('operationcomplete', me, operation);
225 me.runNextOperation();
226 }
227 };
228
229 operation.setStarted();
230
231 me.proxy[operation.action](operation, onProxyReturn, me);
232 }
233
234 return me;
235 }
236});