<!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">// @tag extras,core
|
// @require Format.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:
|
*
|
* task.destroy();
|
*
|
* 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-fireIdleEvent'> /**
|
</span> * @cfg {Boolean} [fireIdleEvent=true]
|
* This may be configured `false` to inhibit firing of the {@link Ext.EventManager#idleEvent idle event} after task invocation.
|
*/
|
|
<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 = Ext.Date.now();
|
|
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);
|
},
|
|
<span id='Ext-util-TaskRunner-property-firing'> //-------------------------------------------------------------------------
|
</span>
|
firing: false,
|
|
<span id='Ext-util-TaskRunner-property-nextExpires'> nextExpires: 1e99,
|
</span>
|
<span id='Ext-util-TaskRunner-method-onTick'> // private
|
</span> onTick: function () {
|
var me = this,
|
tasks = me.tasks,
|
now = Ext.Date.now(),
|
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 {
|
// <debug>
|
Ext.log({
|
msg: taskError,
|
level: 'error'
|
});
|
// </debug>
|
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, Ext.Date.now());
|
}
|
|
// After a tick
|
if (me.fireIdleEvent !== false) {
|
Ext.EventManager.idleEvent.fire();
|
}
|
},
|
|
<span id='Ext-util-TaskRunner-method-startTimer'> // private
|
</span> 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;
|
|
// Documented in TaskManager.js
|
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>
|