mirror of
https://github.com/shaka-project/shaka-player.git
synced 2026-06-14 15:56:38 +03:00
4cc4e96dbd
* Updates all Copyright years to 2015. * Adds licenses annotations to all JS. * Makes all licenses identical to avoid repeated appearance in the compiled output. * Drops fileoverview annotations, which do not affect docs output. * The linter still requires fileoverview on externs. This patch required a newer closure compiler, since the previous version we used had a bug regarding license annotations that caused the license comment block to appear in the output once per file regardless of uniqueness. Change-Id: I2e9272db680cba7ecc4613d97f1d3a94ac2244cc
225 lines
5.9 KiB
JavaScript
225 lines
5.9 KiB
JavaScript
/**
|
|
* @license
|
|
* Copyright 2015 Google Inc.
|
|
*
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
* you may not use this file except in compliance with the License.
|
|
* You may obtain a copy of the License at
|
|
*
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
*
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
* See the License for the specific language governing permissions and
|
|
* limitations under the License.
|
|
*/
|
|
|
|
goog.provide('shaka.util.Task');
|
|
|
|
goog.require('shaka.asserts');
|
|
goog.require('shaka.util.PublicPromise');
|
|
|
|
|
|
|
|
/**
|
|
* A utility to create abortable, multi-stage tasks based on Promises.
|
|
* @constructor
|
|
*/
|
|
shaka.util.Task = function() {
|
|
/** @private {!shaka.util.PublicPromise} */
|
|
this.taskPromise_ = new shaka.util.PublicPromise();
|
|
|
|
/** @private {boolean} */
|
|
this.started_ = false;
|
|
|
|
/** @private {shaka.util.PublicPromise} */
|
|
this.abortedPromise_ = null;
|
|
|
|
/** @private {!Array.<shaka.util.Task.StageFunction>} */
|
|
this.stages_ = [];
|
|
|
|
/** @private {?function()} */
|
|
this.aborter_ = null;
|
|
};
|
|
|
|
|
|
/** @typedef {function(?):(Array|undefined)} */
|
|
shaka.util.Task.StageFunction;
|
|
|
|
|
|
/**
|
|
* Adds a new stage to the task. Should only be used before starting the task.
|
|
*
|
|
* A stage function should return either nothing or an Array with two items in
|
|
* it.
|
|
*
|
|
* If the stage function returns nothing, this stage is always successful and
|
|
* completes right away. No data will be passed to the next stage.
|
|
*
|
|
* If the stage function returns an Array, the first item should be a Promise
|
|
* which is resolved or rejected when the stage completes. If this promise is
|
|
* rejected, the task has failed and the task's 'catch' functions are called.
|
|
*
|
|
* The second item in the Array should be a function which aborts this stage
|
|
* of the operation. If this is omitted, then the stage cannot be terminated
|
|
* early, and aborting the Task during this stage means waiting for the end of
|
|
* the stage.
|
|
*
|
|
* @param {shaka.util.Task.StageFunction} fn The next stage of the task.
|
|
* @throws {Error} if the task has been started.
|
|
*/
|
|
shaka.util.Task.prototype.append = function(fn) {
|
|
if (this.started_) {
|
|
throw new Error('Cannot append to a running task!');
|
|
}
|
|
this.stages_.push(fn);
|
|
};
|
|
|
|
|
|
/**
|
|
* Starts the task.
|
|
* @throws {Error} if the task has already been started.
|
|
*/
|
|
shaka.util.Task.prototype.start = function() {
|
|
if (this.started_) {
|
|
throw new Error('Task already started!');
|
|
}
|
|
this.started_ = true;
|
|
// The first 'stage' is an empty function, which ensures two things:
|
|
// 1. All real stages execute asynchronously.
|
|
// 2. It is always safe to call startNextStage_ at least once.
|
|
this.stages_.unshift(function() {});
|
|
this.startNextStage_(undefined);
|
|
};
|
|
|
|
|
|
/**
|
|
* Abort the task and run all 'catch' handlers.
|
|
* The caught error will have type 'aborted'.
|
|
* @return {!Promise} resolved once the task is aborted.
|
|
*/
|
|
shaka.util.Task.prototype.abort = function() {
|
|
if (this.abortedPromise_) {
|
|
return this.abortedPromise_;
|
|
}
|
|
|
|
if (!this.started_) {
|
|
this.started_ = true;
|
|
return Promise.resolve();
|
|
}
|
|
|
|
if (this.aborter_) {
|
|
this.aborter_();
|
|
}
|
|
|
|
this.abortedPromise_ = new shaka.util.PublicPromise();
|
|
return this.abortedPromise_;
|
|
};
|
|
|
|
|
|
/**
|
|
* End the running task. No more stages will be executed, and this will not
|
|
* be considered an error. Should always be called from within a stage.
|
|
*/
|
|
shaka.util.Task.prototype.end = function() {
|
|
// Forget all stages after this one.
|
|
this.stages_.splice(1);
|
|
};
|
|
|
|
|
|
/**
|
|
* Get a promise which represents the entire task.
|
|
* @return {!Promise}
|
|
*/
|
|
shaka.util.Task.prototype.getPromise = function() {
|
|
return this.taskPromise_;
|
|
};
|
|
|
|
|
|
/**
|
|
* Start the next stage of the task.
|
|
* @param {?} arg passed to the next stage.
|
|
* @private
|
|
*/
|
|
shaka.util.Task.prototype.startNextStage_ = function(arg) {
|
|
var retval = this.stages_[0](arg);
|
|
|
|
var done;
|
|
if (retval) {
|
|
shaka.asserts.assert(retval.length == 1 || retval.length == 2);
|
|
done = retval[0];
|
|
shaka.asserts.assert(done);
|
|
this.aborter_ = retval[1];
|
|
} else {
|
|
done = Promise.resolve();
|
|
this.aborter_ = null;
|
|
}
|
|
|
|
done.then(shaka.util.TypedBind(this,
|
|
/** @param {?} arg */
|
|
function(arg) {
|
|
if (this.abortedPromise_) {
|
|
// Aborted in between stages or in a way that didn't fail the stage.
|
|
// Clean up.
|
|
this.stages_ = [];
|
|
this.aborter_ = null;
|
|
this.completeAbort_();
|
|
return;
|
|
}
|
|
|
|
// Throw away the stage we just completed.
|
|
this.stages_.shift();
|
|
|
|
if (this.stages_.length) {
|
|
// Start the next stage.
|
|
this.startNextStage_(arg);
|
|
} else {
|
|
// All done. Clean up.
|
|
this.taskPromise_.resolve(arg);
|
|
this.aborter_ = null;
|
|
}
|
|
})
|
|
).catch(shaka.util.TypedBind(this,
|
|
/** @param {*} error */
|
|
function(error) {
|
|
// Task failed. Clean up.
|
|
this.stages_ = [];
|
|
this.aborter_ = null;
|
|
|
|
if (this.abortedPromise_) {
|
|
// Aborted during a stage in a way that failed the stage.
|
|
// Resolve the aborted promise.
|
|
this.completeAbort_();
|
|
} else {
|
|
this.taskPromise_.reject(error);
|
|
}
|
|
})
|
|
);
|
|
};
|
|
|
|
|
|
/**
|
|
* Rejects the task promise and then resolves the abort promise.
|
|
*
|
|
* @private
|
|
*/
|
|
shaka.util.Task.prototype.completeAbort_ = function() {
|
|
shaka.asserts.assert(this.taskPromise_);
|
|
shaka.asserts.assert(this.abortedPromise_);
|
|
|
|
var error = new Error('Task aborted.');
|
|
error.type = 'aborted';
|
|
this.taskPromise_.reject(error);
|
|
|
|
// Ensure the abort promise is resolved after the task promise is rejected.
|
|
// This allows callers to make some simplifying assumptions on the ordering
|
|
// of async callbacks.
|
|
window.setTimeout(
|
|
function() {
|
|
this.abortedPromise_.resolve();
|
|
this.abortedPromise_ = null;
|
|
}.bind(this), 5);
|
|
};
|
|
|