PageRenderTime 52ms CodeModel.GetById 17ms app.highlight 16ms RepoModel.GetById 1ms app.codeStats 0ms

/ext-4.1.0_b3/src/data/Batch.js

https://bitbucket.org/srogerf/javascript
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});