-
Notifications
You must be signed in to change notification settings - Fork 2
Asynchronous Decisions
Most of the decisions made in the player are asynchronous. We can’t know how any particular, even the smallest, will be made. For example… When deciding whether or not more data should be buffered, the decision could be quick (is the amount currently buffered less than Manifest.minBufferTime?) or it could be a complex operation that requires information from a server. Perhaps more data will be buffered if the player detects that the user’s latency is higher. Or perhaps during a particularly high-traffic portion of the video the player will buffer more data than usual.
To make the asynchronous logic as easy as possible the player is using a Promises framework. https://github.com/kriskowal/q
The general idea is that every decision returns a Promise object which will be fulfilled at a later point in time. When the promise is created a callback is added to be called when the promise is fulfilled.
This allows the decision to be asynchronous while letting the code be organized in a way that has some semblance of order.
To create and return a promise, a deferred object must first be created. This object contains the promise that will be returned. Later, when a value has been produced, the deferred object can be resolved, which will fulfill the promise callback. It’s even OK to resolve a deferred instance before returning the promise. As soon as a callback is added to the promise it will be fulfilled if the deferred instance was already resolved. A deferred instance can also be rejected should the promise cannot be fulfilled. This will trigger the fail callback (if any) registered with the promise.
Here’s a very basic example:
function getPromise () {
var deferred = Q.defer();
deferred.resolve(value);
return deferred.promise;
}
function doSomething () {
getPromise().then(
function (returnValue) {
console.log("Got the promise value!");
}
);
}
If a decision does not need to be asynchronous, the framework has a method that returns a synchronous promise. This method is Q.when(value)
.
Here’s a slightly more real example:
function shouldLoadNextFragment () {
var result = (this.bufferLength < manifest.minBufferTime);
Q.when(result);
}
function loadNextFragment () {
var deferred = Q.defer(),
req;
fragmentHandler.getUrlForTime(this.currentTime).then(
function (url) {
req = new XMLHttpRequest();
req.responseType = "arraybuffer";
req.open("GET", url, true);
req.onload = function () {
deferred.resolve(req.response);
};
req.onerror = function () {
deferred.reject("Error loading fragment");
}
}
);
return deferred.promise;
}
function doLoadIfRequired () {
shouldLoadNextFragment().then(
function (result) {
if (result) {
return loadNextFragment();
} else {
return Q.when(null);
}
}
).then(
function (data) {
if (data !== null) {
console.log("Loaded a fragment!");
} else {
console.log("Did not load a fragment.");
}
}
);
}