| <!DOCTYPE html> |
| <!-- |
| Copyright (c) 2013 The Chromium Authors. All rights reserved. |
| Use of this source code is governed by a BSD-style license that can be |
| found in the LICENSE file. |
| --> |
| <link rel="import" href="/base/raf.html"> |
| |
| <script> |
| 'use strict'; |
| |
| tv.exportTo('tv.b', function() { |
| /** |
| * A task is a combination of a run callback, a set of subtasks, and an after |
| * task. |
| * |
| * When executed, a task does the following things: |
| * 1. Runs its callback |
| * 2. Runs its subtasks |
| * 3. Runs its after callback. |
| * |
| * The list of subtasks and after task can be mutated inside step #1 but as |
| * soon as the task's callback returns, the subtask list and after task is |
| * fixed and cannot be changed again. |
| * |
| * Use task.after().after().after() to describe the toplevel passes that make |
| * up your computation. Then, use subTasks to add detail to each subtask as it |
| * runs. For example: |
| * var pieces = []; |
| * taskA = new Task(function() { pieces = getPieces(); }); |
| * taskA.after(function(taskA) { |
| * pieces.forEach(function(piece) { |
| * taskA.subTask(function(taskB) { piece.process(); }, this); |
| * }); |
| * }); |
| * |
| * @constructor |
| */ |
| function Task(runCb, thisArg) { |
| if (runCb !== undefined && thisArg === undefined) |
| throw new Error('Almost certainly, you meant to pass a thisArg.'); |
| this.runCb_ = runCb; |
| this.thisArg_ = thisArg; |
| this.afterTask_ = undefined; |
| this.subTasks_ = []; |
| } |
| |
| Task.prototype = { |
| /* |
| * See constructor documentation on semantics of subtasks. |
| */ |
| subTask: function(cb, thisArg) { |
| if (cb instanceof Task) |
| this.subTasks_.push(cb); |
| else |
| this.subTasks_.push(new Task(cb, thisArg)); |
| return this.subTasks_[this.subTasks_.length - 1]; |
| }, |
| |
| /** |
| * Runs the current task and returns the task that should be executed next. |
| */ |
| run: function() { |
| if (this.runCb_ !== undefined) |
| this.runCb_.call(this.thisArg_, this); |
| var subTasks = this.subTasks_; |
| this.subTasks_ = undefined; // Prevent more subTasks from being posted. |
| |
| if (!subTasks.length) |
| return this.afterTask_; |
| |
| // If there are subtasks, then we want to execute all the subtasks and |
| // then this task's afterTask. To make this happen, we update the |
| // afterTask of all the subtasks so the point upward to each other, e.g. |
| // subTask[0].afterTask to subTask[1] and so on. Then, the last subTask's |
| // afterTask points at this task's afterTask. |
| for (var i = 1; i < subTasks.length; i++) |
| subTasks[i - 1].afterTask_ = subTasks[i]; |
| subTasks[subTasks.length - 1].afterTask_ = this.afterTask_; |
| return subTasks[0]; |
| }, |
| |
| /* |
| * See constructor documentation on semantics of after tasks. |
| */ |
| after: function(cb, thisArg) { |
| if (this.afterTask_) |
| throw new Error('Has an after task already'); |
| if (cb instanceof Task) |
| this.afterTask_ = cb; |
| else |
| this.afterTask_ = new Task(cb, thisArg); |
| return this.afterTask_; |
| } |
| }; |
| |
| Task.RunSynchronously = function(task) { |
| var curTask = task; |
| while (curTask) |
| curTask = curTask.run(); |
| } |
| |
| /** |
| * Runs a task using raf.requestIdleCallback, returning |
| * a promise for its completion. |
| */ |
| Task.RunWhenIdle = function(task) { |
| return new Promise(function(resolve, reject) { |
| var curTask = task; |
| function runAnother() { |
| try { |
| curTask = curTask.run(); |
| } catch (e) { |
| reject(e); |
| console.error(e.stack); |
| return; |
| } |
| |
| if (curTask) { |
| tv.b.requestIdleCallback(runAnother); |
| return; |
| } |
| |
| resolve(); |
| } |
| tv.b.requestIdleCallback(runAnother); |
| }); |
| } |
| |
| return { |
| Task: Task |
| }; |
| }); |
| </script> |