1<!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="/tracing/base/raf.html"> 8<link rel="import" href="/tracing/base/timing.html"> 9 10<script> 11'use strict'; 12 13tr.exportTo('tr.b', function() { 14 var Timing = tr.b.Timing; 15 /** 16 * A task is a combination of a run callback, a set of subtasks, and an after 17 * task. 18 * 19 * When executed, a task does the following things: 20 * 1. Runs its callback 21 * 2. Runs its subtasks 22 * 3. Runs its after callback. 23 * 24 * The list of subtasks and after task can be mutated inside step #1 but as 25 * soon as the task's callback returns, the subtask list and after task is 26 * fixed and cannot be changed again. 27 * 28 * Use task.after().after().after() to describe the toplevel passes that make 29 * up your computation. Then, use subTasks to add detail to each subtask as it 30 * runs. For example: 31 * var pieces = []; 32 * taskA = new Task(function() { pieces = getPieces(); }); 33 * taskA.after(function(taskA) { 34 * pieces.forEach(function(piece) { 35 * taskA.subTask(function(taskB) { piece.process(); }, this); 36 * }); 37 * }); 38 * 39 * @constructor 40 */ 41 function Task(runCb, thisArg) { 42 if (runCb !== undefined && thisArg === undefined) 43 throw new Error('Almost certainly, you meant to pass a thisArg.'); 44 this.runCb_ = runCb; 45 this.thisArg_ = thisArg; 46 this.afterTask_ = undefined; 47 this.subTasks_ = []; 48 } 49 50 Task.prototype = { 51 get name() { 52 return this.runCb_.name; 53 }, 54 55 /* 56 * See constructor documentation on semantics of subtasks. 57 */ 58 subTask: function(cb, thisArg) { 59 if (cb instanceof Task) 60 this.subTasks_.push(cb); 61 else 62 this.subTasks_.push(new Task(cb, thisArg)); 63 return this.subTasks_[this.subTasks_.length - 1]; 64 }, 65 66 /** 67 * Runs the current task and returns the task that should be executed next. 68 */ 69 run: function() { 70 if (this.runCb_ !== undefined) 71 this.runCb_.call(this.thisArg_, this); 72 var subTasks = this.subTasks_; 73 this.subTasks_ = undefined; // Prevent more subTasks from being posted. 74 75 if (!subTasks.length) 76 return this.afterTask_; 77 78 // If there are subtasks, then we want to execute all the subtasks and 79 // then this task's afterTask. To make this happen, we update the 80 // afterTask of all the subtasks so the point upward to each other, e.g. 81 // subTask[0].afterTask to subTask[1] and so on. Then, the last subTask's 82 // afterTask points at this task's afterTask. 83 for (var i = 1; i < subTasks.length; i++) 84 subTasks[i - 1].afterTask_ = subTasks[i]; 85 subTasks[subTasks.length - 1].afterTask_ = this.afterTask_; 86 return subTasks[0]; 87 }, 88 89 /* 90 * See constructor documentation on semantics of after tasks. 91 */ 92 after: function(cb, thisArg) { 93 if (this.afterTask_) 94 throw new Error('Has an after task already'); 95 if (cb instanceof Task) 96 this.afterTask_ = cb; 97 else 98 this.afterTask_ = new Task(cb, thisArg); 99 return this.afterTask_; 100 }, 101 102 /* 103 * See constructor documentation on semantics of after tasks. 104 * Note: timedAfter doesn't work when a task throws an exception. 105 * This is because task system doesn't support catching currently. 106 * At the time of writing, this is considered to be an acceptable tradeoff. 107 */ 108 timedAfter: function(groupName, cb, thisArg, opt_args) { 109 if (cb.name === '') 110 throw new Error('Anonymous Task is not allowed'); 111 return this.namedTimedAfter(groupName, cb.name, cb, thisArg, opt_args); 112 }, 113 114 /* 115 * See constructor documentation on semantics of after tasks. 116 * Note: namedTimedAfter doesn't work when a task throws an exception. 117 * This is because task system doesn't support catching currently. 118 * At the time of writing, this is considered to be an acceptable tradeoff. 119 */ 120 namedTimedAfter: function(groupName, name, cb, thisArg, opt_args) { 121 if (this.afterTask_) 122 throw new Error('Has an after task already'); 123 var realTask; 124 if (cb instanceof Task) 125 realTask = cb; 126 else 127 realTask = new Task(cb, thisArg); 128 this.afterTask_ = new Task(function(task) { 129 var markedTask = Timing.mark(groupName, name, opt_args); 130 task.subTask(realTask, thisArg); 131 task.subTask(function() { 132 markedTask.end(); 133 }, thisArg); 134 }, thisArg); 135 return this.afterTask_; 136 }, 137 138 /* 139 * Adds a task after the chain of tasks. 140 */ 141 enqueue: function(cb, thisArg) { 142 var lastTask = this; 143 while (lastTask.afterTask_) 144 lastTask = lastTask.afterTask_; 145 return lastTask.after(cb, thisArg); 146 } 147 }; 148 149 Task.RunSynchronously = function(task) { 150 var curTask = task; 151 while (curTask) 152 curTask = curTask.run(); 153 } 154 155 /** 156 * Runs a task using raf.requestIdleCallback, returning 157 * a promise for its completion. 158 */ 159 Task.RunWhenIdle = function(task) { 160 return new Promise(function(resolve, reject) { 161 var curTask = task; 162 function runAnother() { 163 try { 164 curTask = curTask.run(); 165 } catch (e) { 166 reject(e); 167 console.error(e.stack); 168 return; 169 } 170 171 if (curTask) { 172 tr.b.requestIdleCallback(runAnother); 173 return; 174 } 175 176 resolve(); 177 } 178 tr.b.requestIdleCallback(runAnother); 179 }); 180 } 181 182 return { 183 Task: Task 184 }; 185}); 186</script> 187