TaskManager.html 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437
  1. <!DOCTYPE html>
  2. <html>
  3. <head>
  4. <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
  5. <title>The source code</title>
  6. <link href="../resources/prettify/prettify.css" type="text/css" rel="stylesheet" />
  7. <script type="text/javascript" src="../resources/prettify/prettify.js"></script>
  8. <style type="text/css">
  9. .highlight { display: block; background-color: #ddd; }
  10. </style>
  11. <script type="text/javascript">
  12. function highlight() {
  13. document.getElementById(location.hash.replace(/#/, "")).className = "highlight";
  14. }
  15. </script>
  16. </head>
  17. <body onload="prettyPrint(); highlight();">
  18. <pre class="prettyprint lang-js"><span id='Ext-util-TaskRunner-method-constructor'><span id='Ext-util-TaskRunner'>/**
  19. </span></span> * Provides the ability to execute one or more arbitrary tasks in a asynchronous manner.
  20. * Generally, you can use the singleton {@link Ext.TaskManager} instead, but if needed,
  21. * you can create separate instances of TaskRunner. Any number of separate tasks can be
  22. * started at any time and will run independently of each other.
  23. *
  24. * Example usage:
  25. *
  26. * // Start a simple clock task that updates a div once per second
  27. * var updateClock = function () {
  28. * Ext.fly('clock').update(new Date().format('g:i:s A'));
  29. * }
  30. *
  31. * var runner = new Ext.util.TaskRunner();
  32. * var task = runner.start({
  33. * run: updateClock,
  34. * interval: 1000
  35. * }
  36. *
  37. * The equivalent using TaskManager:
  38. *
  39. * var task = Ext.TaskManager.start({
  40. * run: updateClock,
  41. * interval: 1000
  42. * });
  43. *
  44. * To end a running task:
  45. *
  46. * Ext.TaskManager.stop(task);
  47. *
  48. * If a task needs to be started and stopped repeated over time, you can create a
  49. * {@link Ext.util.TaskRunner.Task Task} instance.
  50. *
  51. * var task = runner.newTask({
  52. * run: function () {
  53. * // useful code
  54. * },
  55. * interval: 1000
  56. * });
  57. *
  58. * task.start();
  59. *
  60. * // ...
  61. *
  62. * task.stop();
  63. *
  64. * // ...
  65. *
  66. * task.start();
  67. *
  68. * A re-usable, one-shot task can be managed similar to the above:
  69. *
  70. * var task = runner.newTask({
  71. * run: function () {
  72. * // useful code to run once
  73. * },
  74. * repeat: 1
  75. * });
  76. *
  77. * task.start();
  78. *
  79. * // ...
  80. *
  81. * task.start();
  82. *
  83. * See the {@link #start} method for details about how to configure a task object.
  84. *
  85. * Also see {@link Ext.util.DelayedTask}.
  86. *
  87. * @constructor
  88. * @param {Number/Object} [interval=10] The minimum precision in milliseconds supported by this
  89. * TaskRunner instance. Alternatively, a config object to apply to the new instance.
  90. */
  91. Ext.define('Ext.util.TaskRunner', {
  92. <span id='Ext-util-TaskRunner-cfg-interval'> /**
  93. </span> * @cfg interval
  94. * The timer resolution.
  95. */
  96. interval: 10,
  97. <span id='Ext-util-TaskRunner-property-timerId'> /**
  98. </span> * @property timerId
  99. * The id of the current timer.
  100. * @private
  101. */
  102. timerId: null,
  103. constructor: function (interval) {
  104. var me = this;
  105. if (typeof interval == 'number') {
  106. me.interval = interval;
  107. } else if (interval) {
  108. Ext.apply(me, interval);
  109. }
  110. me.tasks = [];
  111. me.timerFn = Ext.Function.bind(me.onTick, me);
  112. },
  113. <span id='Ext-util-TaskRunner-method-newTask'> /**
  114. </span> * Creates a new {@link Ext.util.TaskRunner.Task Task} instance. These instances can
  115. * be easily started and stopped.
  116. * @param {Object} config The config object. For details on the supported properties,
  117. * see {@link #start}.
  118. */
  119. newTask: function (config) {
  120. var task = new Ext.util.TaskRunner.Task(config);
  121. task.manager = this;
  122. return task;
  123. },
  124. <span id='Ext-util-TaskRunner-method-start'> /**
  125. </span> * Starts a new task.
  126. *
  127. * Before each invocation, Ext injects the property `taskRunCount` into the task object
  128. * so that calculations based on the repeat count can be performed.
  129. *
  130. * The returned task will contain a `destroy` method that can be used to destroy the
  131. * task and cancel further calls. This is equivalent to the {@link #stop} method.
  132. *
  133. * @param {Object} task A config object that supports the following properties:
  134. * @param {Function} task.run The function to execute each time the task is invoked. The
  135. * function will be called at each interval and passed the `args` argument if specified,
  136. * and the current invocation count if not.
  137. *
  138. * If a particular scope (`this` reference) is required, be sure to specify it using
  139. * the `scope` argument.
  140. *
  141. * @param {Function} task.onError The function to execute in case of unhandled
  142. * error on task.run.
  143. *
  144. * @param {Boolean} task.run.return `false` from this function to terminate the task.
  145. *
  146. * @param {Number} task.interval The frequency in milliseconds with which the task
  147. * should be invoked.
  148. *
  149. * @param {Object[]} task.args An array of arguments to be passed to the function
  150. * specified by `run`. If not specified, the current invocation count is passed.
  151. *
  152. * @param {Object} task.scope The scope (`this` reference) in which to execute the
  153. * `run` function. Defaults to the task config object.
  154. *
  155. * @param {Number} task.duration The length of time in milliseconds to invoke the task
  156. * before stopping automatically (defaults to indefinite).
  157. *
  158. * @param {Number} task.repeat The number of times to invoke the task before stopping
  159. * automatically (defaults to indefinite).
  160. * @return {Object} The task
  161. */
  162. start: function(task) {
  163. var me = this,
  164. now = new Date().getTime();
  165. if (!task.pending) {
  166. me.tasks.push(task);
  167. task.pending = true; // don't allow the task to be added to me.tasks again
  168. }
  169. task.stopped = false; // might have been previously stopped...
  170. task.taskStartTime = now;
  171. task.taskRunTime = task.fireOnStart !== false ? 0 : task.taskStartTime;
  172. task.taskRunCount = 0;
  173. if (!me.firing) {
  174. if (task.fireOnStart !== false) {
  175. me.startTimer(0, now);
  176. } else {
  177. me.startTimer(task.interval, now);
  178. }
  179. }
  180. return task;
  181. },
  182. <span id='Ext-util-TaskRunner-method-stop'> /**
  183. </span> * Stops an existing running task.
  184. * @param {Object} task The task to stop
  185. * @return {Object} The task
  186. */
  187. stop: function(task) {
  188. // NOTE: we don't attempt to remove the task from me.tasks at this point because
  189. // this could be called from inside a task which would then corrupt the state of
  190. // the loop in onTick
  191. if (!task.stopped) {
  192. task.stopped = true;
  193. if (task.onStop) {
  194. task.onStop.call(task.scope || task, task);
  195. }
  196. }
  197. return task;
  198. },
  199. <span id='Ext-util-TaskRunner-method-stopAll'> /**
  200. </span> * Stops all tasks that are currently running.
  201. */
  202. stopAll: function() {
  203. // onTick will take care of cleaning up the mess after this point...
  204. Ext.each(this.tasks, this.stop, this);
  205. },
  206. //-------------------------------------------------------------------------
  207. firing: false,
  208. nextExpires: 1e99,
  209. // private
  210. onTick: function () {
  211. var me = this,
  212. tasks = me.tasks,
  213. now = new Date().getTime(),
  214. nextExpires = 1e99,
  215. len = tasks.length,
  216. expires, newTasks, i, task, rt, remove;
  217. me.timerId = null;
  218. me.firing = true; // ensure we don't startTimer during this loop...
  219. // tasks.length can be &gt; len if start is called during a task.run call... so we
  220. // first check len to avoid tasks.length reference but eventually we need to also
  221. // check tasks.length. we avoid repeating use of tasks.length by setting len at
  222. // that time (to help the next loop)
  223. for (i = 0; i &lt; len || i &lt; (len = tasks.length); ++i) {
  224. task = tasks[i];
  225. if (!(remove = task.stopped)) {
  226. expires = task.taskRunTime + task.interval;
  227. if (expires &lt;= now) {
  228. rt = 1; // otherwise we have a stale &quot;rt&quot;
  229. try {
  230. rt = task.run.apply(task.scope || task, task.args || [++task.taskRunCount]);
  231. } catch (taskError) {
  232. try {
  233. if (task.onError) {
  234. rt = task.onError.call(task.scope || task, task, taskError);
  235. }
  236. } catch (ignore) { }
  237. }
  238. task.taskRunTime = now;
  239. if (rt === false || task.taskRunCount === task.repeat) {
  240. me.stop(task);
  241. remove = true;
  242. } else {
  243. remove = task.stopped; // in case stop was called by run
  244. expires = now + task.interval;
  245. }
  246. }
  247. if (!remove &amp;&amp; task.duration &amp;&amp; task.duration &lt;= (now - task.taskStartTime)) {
  248. me.stop(task);
  249. remove = true;
  250. }
  251. }
  252. if (remove) {
  253. task.pending = false; // allow the task to be added to me.tasks again
  254. // once we detect that a task needs to be removed, we copy the tasks that
  255. // will carry forward into newTasks... this way we avoid O(N*N) to remove
  256. // each task from the tasks array (and ripple the array down) and also the
  257. // potentially wasted effort of making a new tasks[] even if all tasks are
  258. // going into the next wave.
  259. if (!newTasks) {
  260. newTasks = tasks.slice(0, i);
  261. // we don't set me.tasks here because callbacks can also start tasks,
  262. // which get added to me.tasks... so we will visit them in this loop
  263. // and account for their expirations in nextExpires...
  264. }
  265. } else {
  266. if (newTasks) {
  267. newTasks.push(task); // we've cloned the tasks[], so keep this one...
  268. }
  269. if (nextExpires &gt; expires) {
  270. nextExpires = expires; // track the nearest expiration time
  271. }
  272. }
  273. }
  274. if (newTasks) {
  275. // only now can we copy the newTasks to me.tasks since no user callbacks can
  276. // take place
  277. me.tasks = newTasks;
  278. }
  279. me.firing = false; // we're done, so allow startTimer afterwards
  280. if (me.tasks.length) {
  281. // we create a new Date here because all the callbacks could have taken a long
  282. // time... we want to base the next timeout on the current time (after the
  283. // callback storm):
  284. me.startTimer(nextExpires - now, new Date().getTime());
  285. }
  286. },
  287. // private
  288. startTimer: function (timeout, now) {
  289. var me = this,
  290. expires = now + timeout,
  291. timerId = me.timerId;
  292. // Check to see if this request is enough in advance of the current timer. If so,
  293. // we reschedule the timer based on this new expiration.
  294. if (timerId &amp;&amp; me.nextExpires - expires &gt; me.interval) {
  295. clearTimeout(timerId);
  296. timerId = null;
  297. }
  298. if (!timerId) {
  299. if (timeout &lt; me.interval) {
  300. timeout = me.interval;
  301. }
  302. me.timerId = setTimeout(me.timerFn, timeout);
  303. me.nextExpires = expires;
  304. }
  305. }
  306. },
  307. function () {
  308. var me = this,
  309. proto = me.prototype;
  310. <span id='Ext-util-TaskRunner-method-destroy'> /**
  311. </span> * Destroys this instance, stopping all tasks that are currently running.
  312. * @method destroy
  313. */
  314. proto.destroy = proto.stopAll;
  315. <span id='Ext-TaskManager'> /**
  316. </span> * @class Ext.TaskManager
  317. * @extends Ext.util.TaskRunner
  318. * @singleton
  319. *
  320. * A static {@link Ext.util.TaskRunner} instance that can be used to start and stop
  321. * arbitrary tasks. See {@link Ext.util.TaskRunner} for supported methods and task
  322. * config properties.
  323. *
  324. * // Start a simple clock task that updates a div once per second
  325. * var task = {
  326. * run: function(){
  327. * Ext.fly('clock').update(new Date().format('g:i:s A'));
  328. * },
  329. * interval: 1000 //1 second
  330. * }
  331. *
  332. * Ext.TaskManager.start(task);
  333. *
  334. * See the {@link #start} method for details about how to configure a task object.
  335. */
  336. Ext.util.TaskManager = Ext.TaskManager = new me();
  337. <span id='Ext-util-TaskRunner-Task'> /**
  338. </span> * Instances of this class are created by {@link Ext.util.TaskRunner#newTask} method.
  339. *
  340. * For details on config properties, see {@link Ext.util.TaskRunner#start}.
  341. * @class Ext.util.TaskRunner.Task
  342. */
  343. me.Task = new Ext.Class({
  344. isTask: true,
  345. <span id='Ext-util-TaskRunner-Task-property-stopped'> /**
  346. </span> * This flag is set to `true` by {@link #stop}.
  347. * @private
  348. */
  349. stopped: true, // this avoids the odd combination of !stopped &amp;&amp; !pending
  350. <span id='Ext-util-TaskRunner-Task-property-fireOnStart'> /**
  351. </span> * Override default behavior
  352. */
  353. fireOnStart: false,
  354. constructor: function (config) {
  355. Ext.apply(this, config);
  356. },
  357. <span id='Ext-util-TaskRunner-Task-method-restart'> /**
  358. </span> * Restarts this task, clearing it duration, expiration and run count.
  359. * @param {Number} [interval] Optionally reset this task's interval.
  360. */
  361. restart: function (interval) {
  362. if (interval !== undefined) {
  363. this.interval = interval;
  364. }
  365. this.manager.start(this);
  366. },
  367. <span id='Ext-util-TaskRunner-Task-method-start'> /**
  368. </span> * Starts this task if it is not already started.
  369. * @param {Number} [interval] Optionally reset this task's interval.
  370. */
  371. start: function (interval) {
  372. if (this.stopped) {
  373. this.restart(interval);
  374. }
  375. },
  376. <span id='Ext-util-TaskRunner-Task-method-stop'> /**
  377. </span> * Stops this task.
  378. */
  379. stop: function () {
  380. this.manager.stop(this);
  381. }
  382. });
  383. proto = me.Task.prototype;
  384. <span id='Ext-util-TaskRunner-Task-method-destroy'> /**
  385. </span> * Destroys this instance, stopping this task's execution.
  386. * @method destroy
  387. */
  388. proto.destroy = proto.stop;
  389. });
  390. </pre>
  391. </body>
  392. </html>