1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style license that can be 3 // found in the LICENSE file. 4 5 'use strict'; 6 7 /** 8 * @class FunctionSequence to invoke steps in sequence 9 * 10 * @param {string} name // TODO(JSDOC). 11 * @param {Array} steps array of functions to invoke in sequence. 12 * @param {Object} logger logger. 13 * @param {function} callback callback to invoke on success. 14 * @param {function} failureCallback callback to invoke on failure. 15 * @constructor 16 */ 17 function FunctionSequence(name, steps, logger, callback, failureCallback) { 18 // Private variables hidden in closure 19 this.currentStepIdx_ = -1; 20 this.failed_ = false; 21 this.steps_ = steps; 22 this.callback_ = callback; 23 this.failureCallback_ = failureCallback; 24 this.logger = logger; 25 this.name = name; 26 27 this.onError = this.onError_.bind(this); 28 this.finish = this.finish_.bind(this); 29 this.nextStep = this.nextStep_.bind(this); 30 this.apply = this.apply_.bind(this); 31 } 32 33 /** 34 * Sets new callback 35 * 36 * @param {function} callback new callback to call on succeed. 37 */ 38 FunctionSequence.prototype.setCallback = function(callback) { 39 this.callback_ = callback; 40 }; 41 42 /** 43 * Sets new error callback 44 * 45 * @param {function} failureCallback new callback to call on failure. 46 */ 47 FunctionSequence.prototype.setFailureCallback = function(failureCallback) { 48 this.failureCallback_ = failureCallback; 49 }; 50 51 52 /** 53 * Error handling function, which traces current error step, stops sequence 54 * advancing and fires error callback. 55 * 56 * @param {string} err Error message. 57 * @private 58 */ 59 FunctionSequence.prototype.onError_ = function(err) { 60 this.logger.vlog('Failed step: ' + this.steps_[this.currentStepIdx_].name + 61 ': ' + err); 62 if (!this.failed_) { 63 this.failed_ = true; 64 this.failureCallback_(err); 65 } 66 }; 67 68 /** 69 * Finishes sequence processing and jumps to the last step. 70 * This method should not be used externally. In external 71 * cases should be used finish function, which is defined in closure and thus 72 * has access to internal variables of functionsequence. 73 * @private 74 */ 75 FunctionSequence.prototype.finish_ = function() { 76 if (!this.failed_ && this.currentStepIdx_ < this.steps_.length) { 77 this.currentStepIdx_ = this.steps_.length; 78 this.callback_(); 79 } 80 }; 81 82 /** 83 * Advances to next step. 84 * This method should not be used externally. In external 85 * cases should be used nextStep function, which is defined in closure and thus 86 * has access to internal variables of functionsequence. 87 * @private 88 * @param {...} var_args // TODO(JSDOC). 89 */ 90 FunctionSequence.prototype.nextStep_ = function(var_args) { 91 if (this.failed_) { 92 return; 93 } 94 95 if (++this.currentStepIdx_ >= this.steps_.length) { 96 this.logger.vlog('Sequence ended'); 97 this.callback_.apply(this, arguments); 98 } else { 99 this.logger.vlog('Attempting to start step [' + 100 this.steps_[this.currentStepIdx_].name + 101 ']'); 102 try { 103 this.steps_[this.currentStepIdx_].apply(this, arguments); 104 } catch (e) { 105 this.onError(e.toString()); 106 } 107 } 108 }; 109 110 /** 111 * This function should be called only once on start, so start sequence pipeline 112 * @param {...} var_args // TODO(JSDOC). 113 */ 114 FunctionSequence.prototype.start = function(var_args) { 115 if (this.started) { 116 throw new Error('"Start" method of FunctionSequence was called twice'); 117 } 118 119 this.logger.log('Starting sequence with ' + arguments.length + ' arguments'); 120 121 this.started = true; 122 this.nextStep.apply(this, arguments); 123 }; 124 125 /** 126 * Add Function object mimics to FunctionSequence 127 * @private 128 * @param {*} obj // TODO(JSDOC). 129 * @param {Array.*} args // TODO(JSDOC). 130 */ 131 FunctionSequence.prototype.apply_ = function(obj, args) { 132 this.start.apply(this, args); 133 }; 134