blob: d5a7cbcbe8571316dfe415f350d18ccb30ff304c [file] [log] [blame]
Chris Craikb122baf2015-03-05 13:58:42 -08001<!DOCTYPE html>
2<!--
3Copyright (c) 2013 The Chromium Authors. All rights reserved.
4Use of this source code is governed by a BSD-style license that can be
5found in the LICENSE file.
6-->
7<link rel="import" href="/base/raf.html">
8
9<script>
10'use strict';
11
12tv.exportTo('tv.b', function() {
13 /**
14 * A task is a combination of a run callback, a set of subtasks, and an after
15 * task.
16 *
17 * When executed, a task does the following things:
18 * 1. Runs its callback
19 * 2. Runs its subtasks
20 * 3. Runs its after callback.
21 *
22 * The list of subtasks and after task can be mutated inside step #1 but as
23 * soon as the task's callback returns, the subtask list and after task is
24 * fixed and cannot be changed again.
25 *
26 * Use task.after().after().after() to describe the toplevel passes that make
27 * up your computation. Then, use subTasks to add detail to each subtask as it
28 * runs. For example:
29 * var pieces = [];
30 * taskA = new Task(function() { pieces = getPieces(); });
31 * taskA.after(function(taskA) {
32 * pieces.forEach(function(piece) {
33 * taskA.subTask(function(taskB) { piece.process(); }, this);
34 * });
35 * });
36 *
37 * @constructor
38 */
39 function Task(runCb, thisArg) {
40 if (runCb !== undefined && thisArg === undefined)
41 throw new Error('Almost certainly, you meant to pass a thisArg.');
42 this.runCb_ = runCb;
43 this.thisArg_ = thisArg;
44 this.afterTask_ = undefined;
45 this.subTasks_ = [];
46 }
47
48 Task.prototype = {
49 /*
50 * See constructor documentation on semantics of subtasks.
51 */
52 subTask: function(cb, thisArg) {
53 if (cb instanceof Task)
54 this.subTasks_.push(cb);
55 else
56 this.subTasks_.push(new Task(cb, thisArg));
57 return this.subTasks_[this.subTasks_.length - 1];
58 },
59
60 /**
61 * Runs the current task and returns the task that should be executed next.
62 */
63 run: function() {
64 if (this.runCb_ !== undefined)
65 this.runCb_.call(this.thisArg_, this);
66 var subTasks = this.subTasks_;
67 this.subTasks_ = undefined; // Prevent more subTasks from being posted.
68
69 if (!subTasks.length)
70 return this.afterTask_;
71
72 // If there are subtasks, then we want to execute all the subtasks and
73 // then this task's afterTask. To make this happen, we update the
74 // afterTask of all the subtasks so the point upward to each other, e.g.
75 // subTask[0].afterTask to subTask[1] and so on. Then, the last subTask's
76 // afterTask points at this task's afterTask.
77 for (var i = 1; i < subTasks.length; i++)
78 subTasks[i - 1].afterTask_ = subTasks[i];
79 subTasks[subTasks.length - 1].afterTask_ = this.afterTask_;
80 return subTasks[0];
81 },
82
83 /*
84 * See constructor documentation on semantics of after tasks.
85 */
86 after: function(cb, thisArg) {
87 if (this.afterTask_)
88 throw new Error('Has an after task already');
89 if (cb instanceof Task)
90 this.afterTask_ = cb;
91 else
92 this.afterTask_ = new Task(cb, thisArg);
93 return this.afterTask_;
94 }
95 };
96
97 Task.RunSynchronously = function(task) {
98 var curTask = task;
99 while (curTask)
100 curTask = curTask.run();
101 }
102
103 /**
104 * Runs a task using raf.requestIdleCallback, returning
105 * a promise for its completion.
106 */
107 Task.RunWhenIdle = function(task) {
108 return new Promise(function(resolve, reject) {
109 var curTask = task;
110 function runAnother() {
111 try {
112 curTask = curTask.run();
113 } catch (e) {
114 reject(e);
115 console.error(e.stack);
116 return;
117 }
118
119 if (curTask) {
120 tv.b.requestIdleCallback(runAnother);
121 return;
122 }
123
124 resolve();
125 }
126 tv.b.requestIdleCallback(runAnother);
127 });
128 }
129
130 return {
131 Task: Task
132 };
133});
134</script>