| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437 | <!DOCTYPE html><html><head>  <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />  <title>The source code</title>  <link href="../resources/prettify/prettify.css" type="text/css" rel="stylesheet" />  <script type="text/javascript" src="../resources/prettify/prettify.js"></script>  <style type="text/css">    .highlight { display: block; background-color: #ddd; }  </style>  <script type="text/javascript">    function highlight() {      document.getElementById(location.hash.replace(/#/, "")).className = "highlight";    }  </script></head><body onload="prettyPrint(); highlight();">  <pre class="prettyprint lang-js"><span id='Ext-util-TaskRunner-method-constructor'><span id='Ext-util-TaskRunner'>/**</span></span> * Provides the ability to execute one or more arbitrary tasks in a asynchronous manner. * Generally, you can use the singleton {@link Ext.TaskManager} instead, but if needed, * you can create separate instances of TaskRunner. Any number of separate tasks can be * started at any time and will run independently of each other. *  * Example usage: * *      // Start a simple clock task that updates a div once per second *      var updateClock = function () { *          Ext.fly('clock').update(new Date().format('g:i:s A')); *      } * *      var runner = new Ext.util.TaskRunner(); *      var task = runner.start({ *          run: updateClock, *          interval: 1000 *      } * * The equivalent using TaskManager: * *      var task = Ext.TaskManager.start({ *          run: updateClock, *          interval: 1000 *      }); * * To end a running task: *  *      Ext.TaskManager.stop(task); * * If a task needs to be started and stopped repeated over time, you can create a * {@link Ext.util.TaskRunner.Task Task} instance. * *      var task = runner.newTask({ *          run: function () { *              // useful code *          }, *          interval: 1000 *      }); *       *      task.start(); *       *      // ... *       *      task.stop(); *       *      // ... *       *      task.start(); * * A re-usable, one-shot task can be managed similar to the above: * *      var task = runner.newTask({ *          run: function () { *              // useful code to run once *          }, *          repeat: 1 *      }); *       *      task.start(); *       *      // ... *       *      task.start(); * * See the {@link #start} method for details about how to configure a task object. * * Also see {@link Ext.util.DelayedTask}.  *  * @constructor * @param {Number/Object} [interval=10] The minimum precision in milliseconds supported by this * TaskRunner instance. Alternatively, a config object to apply to the new instance. */Ext.define('Ext.util.TaskRunner', {<span id='Ext-util-TaskRunner-cfg-interval'>    /**</span>     * @cfg interval     * The timer resolution.     */    interval: 10,<span id='Ext-util-TaskRunner-property-timerId'>    /**</span>     * @property timerId     * The id of the current timer.     * @private     */    timerId: null,    constructor: function (interval) {        var me = this;        if (typeof interval == 'number') {            me.interval = interval;        } else if (interval) {            Ext.apply(me, interval);        }        me.tasks = [];        me.timerFn = Ext.Function.bind(me.onTick, me);    },<span id='Ext-util-TaskRunner-method-newTask'>    /**</span>     * Creates a new {@link Ext.util.TaskRunner.Task Task} instance. These instances can     * be easily started and stopped.     * @param {Object} config The config object. For details on the supported properties,     * see {@link #start}.     */    newTask: function (config) {        var task = new Ext.util.TaskRunner.Task(config);        task.manager = this;        return task;    },<span id='Ext-util-TaskRunner-method-start'>    /**</span>     * Starts a new task.     *     * Before each invocation, Ext injects the property `taskRunCount` into the task object     * so that calculations based on the repeat count can be performed.     *      * The returned task will contain a `destroy` method that can be used to destroy the     * task and cancel further calls. This is equivalent to the {@link #stop} method.     *     * @param {Object} task A config object that supports the following properties:     * @param {Function} task.run The function to execute each time the task is invoked. The     * function will be called at each interval and passed the `args` argument if specified,     * and the current invocation count if not.     *      * If a particular scope (`this` reference) is required, be sure to specify it using     * the `scope` argument.     *      * @param {Function} task.onError The function to execute in case of unhandled     * error on task.run.     *     * @param {Boolean} task.run.return `false` from this function to terminate the task.     *     * @param {Number} task.interval The frequency in milliseconds with which the task     * should be invoked.     *     * @param {Object[]} task.args An array of arguments to be passed to the function     * specified by `run`. If not specified, the current invocation count is passed.     *     * @param {Object} task.scope The scope (`this` reference) in which to execute the     * `run` function. Defaults to the task config object.     *     * @param {Number} task.duration The length of time in milliseconds to invoke the task     * before stopping automatically (defaults to indefinite).     *     * @param {Number} task.repeat The number of times to invoke the task before stopping     * automatically (defaults to indefinite).     * @return {Object} The task     */    start: function(task) {        var me = this,            now = new Date().getTime();        if (!task.pending) {            me.tasks.push(task);            task.pending = true; // don't allow the task to be added to me.tasks again        }        task.stopped = false; // might have been previously stopped...        task.taskStartTime = now;        task.taskRunTime = task.fireOnStart !== false ? 0 : task.taskStartTime;        task.taskRunCount = 0;        if (!me.firing) {            if (task.fireOnStart !== false) {                me.startTimer(0, now);            } else {                me.startTimer(task.interval, now);            }        }        return task;    },<span id='Ext-util-TaskRunner-method-stop'>    /**</span>     * Stops an existing running task.     * @param {Object} task The task to stop     * @return {Object} The task     */    stop: function(task) {        // NOTE: we don't attempt to remove the task from me.tasks at this point because        // this could be called from inside a task which would then corrupt the state of        // the loop in onTick        if (!task.stopped) {            task.stopped = true;            if (task.onStop) {                task.onStop.call(task.scope || task, task);            }        }        return task;    },<span id='Ext-util-TaskRunner-method-stopAll'>    /**</span>     * Stops all tasks that are currently running.     */    stopAll: function() {        // onTick will take care of cleaning up the mess after this point...        Ext.each(this.tasks, this.stop, this);    },    //-------------------------------------------------------------------------    firing: false,    nextExpires: 1e99,    // private    onTick: function () {        var me = this,            tasks = me.tasks,            now = new Date().getTime(),            nextExpires = 1e99,            len = tasks.length,            expires, newTasks, i, task, rt, remove;        me.timerId = null;        me.firing = true; // ensure we don't startTimer during this loop...        // tasks.length can be > len if start is called during a task.run call... so we        // first check len to avoid tasks.length reference but eventually we need to also        // check tasks.length. we avoid repeating use of tasks.length by setting len at        // that time (to help the next loop)        for (i = 0; i < len || i < (len = tasks.length); ++i) {            task = tasks[i];            if (!(remove = task.stopped)) {                expires = task.taskRunTime + task.interval;                if (expires <= now) {                    rt = 1; // otherwise we have a stale "rt"                    try {                        rt = task.run.apply(task.scope || task, task.args || [++task.taskRunCount]);                    } catch (taskError) {                        try {                            if (task.onError) {                                rt = task.onError.call(task.scope || task, task, taskError);                            }                        } catch (ignore) { }                    }                    task.taskRunTime = now;                    if (rt === false || task.taskRunCount === task.repeat) {                        me.stop(task);                        remove = true;                    } else {                        remove = task.stopped; // in case stop was called by run                        expires = now + task.interval;                    }                }                if (!remove && task.duration && task.duration <= (now - task.taskStartTime)) {                    me.stop(task);                    remove = true;                }            }            if (remove) {                task.pending = false; // allow the task to be added to me.tasks again                // once we detect that a task needs to be removed, we copy the tasks that                // will carry forward into newTasks... this way we avoid O(N*N) to remove                // each task from the tasks array (and ripple the array down) and also the                // potentially wasted effort of making a new tasks[] even if all tasks are                // going into the next wave.                if (!newTasks) {                    newTasks = tasks.slice(0, i);                    // we don't set me.tasks here because callbacks can also start tasks,                    // which get added to me.tasks... so we will visit them in this loop                    // and account for their expirations in nextExpires...                }            } else {                if (newTasks) {                    newTasks.push(task); // we've cloned the tasks[], so keep this one...                }                if (nextExpires > expires) {                    nextExpires = expires; // track the nearest expiration time                }            }        }        if (newTasks) {            // only now can we copy the newTasks to me.tasks since no user callbacks can            // take place            me.tasks = newTasks;        }        me.firing = false; // we're done, so allow startTimer afterwards        if (me.tasks.length) {            // we create a new Date here because all the callbacks could have taken a long            // time... we want to base the next timeout on the current time (after the            // callback storm):            me.startTimer(nextExpires - now, new Date().getTime());        }    },    // private    startTimer: function (timeout, now) {        var me = this,            expires = now + timeout,            timerId = me.timerId;        // Check to see if this request is enough in advance of the current timer. If so,        // we reschedule the timer based on this new expiration.        if (timerId && me.nextExpires - expires > me.interval) {            clearTimeout(timerId);            timerId = null;        }        if (!timerId) {            if (timeout < me.interval) {                timeout = me.interval;            }            me.timerId = setTimeout(me.timerFn, timeout);            me.nextExpires = expires;        }    }},function () {    var me = this,        proto = me.prototype;<span id='Ext-util-TaskRunner-method-destroy'>    /**</span>     * Destroys this instance, stopping all tasks that are currently running.     * @method destroy     */    proto.destroy = proto.stopAll;<span id='Ext-TaskManager'>    /**</span>    * @class Ext.TaskManager    * @extends Ext.util.TaskRunner    * @singleton    *    * A static {@link Ext.util.TaskRunner} instance that can be used to start and stop    * arbitrary tasks. See {@link Ext.util.TaskRunner} for supported methods and task    * config properties.    *    *    // Start a simple clock task that updates a div once per second    *    var task = {    *       run: function(){    *           Ext.fly('clock').update(new Date().format('g:i:s A'));    *       },    *       interval: 1000 //1 second    *    }    *    *    Ext.TaskManager.start(task);    *    * See the {@link #start} method for details about how to configure a task object.    */    Ext.util.TaskManager = Ext.TaskManager = new me();<span id='Ext-util-TaskRunner-Task'>    /**</span>     * Instances of this class are created by {@link Ext.util.TaskRunner#newTask} method.     *      * For details on config properties, see {@link Ext.util.TaskRunner#start}.     * @class Ext.util.TaskRunner.Task     */    me.Task = new Ext.Class({        isTask: true,<span id='Ext-util-TaskRunner-Task-property-stopped'>        /**</span>         * This flag is set to `true` by {@link #stop}.         * @private         */        stopped: true, // this avoids the odd combination of !stopped && !pending<span id='Ext-util-TaskRunner-Task-property-fireOnStart'>        /**</span>         * Override default behavior         */        fireOnStart: false,        constructor: function (config) {            Ext.apply(this, config);        },<span id='Ext-util-TaskRunner-Task-method-restart'>        /**</span>         * Restarts this task, clearing it duration, expiration and run count.         * @param {Number} [interval] Optionally reset this task's interval.         */        restart: function (interval) {            if (interval !== undefined) {                this.interval = interval;            }            this.manager.start(this);        },<span id='Ext-util-TaskRunner-Task-method-start'>        /**</span>         * Starts this task if it is not already started.         * @param {Number} [interval] Optionally reset this task's interval.         */        start: function (interval) {            if (this.stopped) {                this.restart(interval);            }        },<span id='Ext-util-TaskRunner-Task-method-stop'>        /**</span>         * Stops this task.         */        stop: function () {            this.manager.stop(this);        }    });    proto = me.Task.prototype;<span id='Ext-util-TaskRunner-Task-method-destroy'>    /**</span>     * Destroys this instance, stopping this task's execution.     * @method destroy     */    proto.destroy = proto.stop;});</pre></body></html>
 |