mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-11-02 15:15:23 +00:00
219 lines
8.0 KiB
JavaScript
219 lines
8.0 KiB
JavaScript
/* vim:set ts=2 sw=2 sts=2 expandtab */
|
|
/*jshint undef: true es5: true node: true browser: true devel: true
|
|
forin: true latedef: false */
|
|
/*global define: true, Cu: true, __URI__: true */
|
|
;(function(id, factory) { // Module boilerplate :(
|
|
if (typeof(define) === 'function') { // RequireJS
|
|
define(factory);
|
|
} else if (typeof(require) === 'function') { // CommonJS
|
|
factory.call(this, require, exports, module);
|
|
} else if (String(this).indexOf('BackstagePass') >= 0) { // JSM
|
|
this[factory.name] = {};
|
|
factory(function require(uri) {
|
|
var imports = {};
|
|
this['Components'].utils.import(uri, imports);
|
|
return imports;
|
|
}, this[factory.name], { uri: __URI__, id: id });
|
|
this.EXPORTED_SYMBOLS = [factory.name];
|
|
} else { // Browser or alike
|
|
var globals = this;
|
|
factory(function require(id) {
|
|
return globals[id];
|
|
}, (globals[id] = {}), { uri: document.location.href + '#' + id, id: id });
|
|
}
|
|
}).call(this, 'promise/core', function Promise(require, exports, module) {
|
|
|
|
'use strict';
|
|
|
|
/**
|
|
* Internal utility: Wraps given `value` into simplified promise, successfully
|
|
* fulfilled to a given `value`. Note the result is not a complete promise
|
|
* implementation, as its method `then` does not returns anything.
|
|
*/
|
|
function fulfilled(value) {
|
|
return { then: function then(fulfill) { fulfill(value); } };
|
|
}
|
|
|
|
/**
|
|
* Internal utility: Wraps given input into simplified promise, pre-rejected
|
|
* with a given `reason`. Note the result is not a complete promise
|
|
* implementation, as its method `then` does not returns anything.
|
|
*/
|
|
function rejected(reason) {
|
|
return { then: function then(fulfill, reject) { reject(reason); } };
|
|
}
|
|
|
|
/**
|
|
* Internal utility: Decorates given `f` function, so that on exception promise
|
|
* rejected with thrown error is returned.
|
|
*/
|
|
function attempt(f) {
|
|
return function effort(input) {
|
|
try {
|
|
return f(input);
|
|
}
|
|
catch(error) {
|
|
return rejected(error);
|
|
}
|
|
};
|
|
}
|
|
|
|
/**
|
|
* Internal utility: Returns `true` if given `value` is a promise. Value is
|
|
* assumed to be a promise if it implements method `then`.
|
|
*/
|
|
function isPromise(value) {
|
|
return value && typeof(value.then) === 'function';
|
|
}
|
|
|
|
/**
|
|
* Creates deferred object containing fresh promise & methods to either resolve
|
|
* or reject it. The result is an object with the following properties:
|
|
* - `promise` Eventual value representation implementing CommonJS [Promises/A]
|
|
* (http://wiki.commonjs.org/wiki/Promises/A) API.
|
|
* - `resolve` Single shot function that resolves enclosed `promise` with a
|
|
* given `value`.
|
|
* - `reject` Single shot function that rejects enclosed `promise` with a given
|
|
* `reason`.
|
|
*
|
|
* ## Example
|
|
*
|
|
* function fetchURI(uri, type) {
|
|
* var deferred = defer();
|
|
* var request = new XMLHttpRequest();
|
|
* request.open("GET", uri, true);
|
|
* request.responseType = type;
|
|
* request.onload = function onload() {
|
|
* deferred.resolve(request.response);
|
|
* }
|
|
* request.onerror = function(event) {
|
|
* deferred.reject(event);
|
|
* }
|
|
* request.send();
|
|
*
|
|
* return deferred.promise;
|
|
* }
|
|
*/
|
|
function defer() {
|
|
// Define FIFO queue of observer pairs. Once promise is resolved & all queued
|
|
// observers are forwarded to `result` and variable is set to `null`.
|
|
var observers = [];
|
|
|
|
// Promise `result`, which will be assigned a resolution value once promise
|
|
// is resolved. Note that result will always be assigned promise (or alike)
|
|
// object to take care of propagation through promise chains. If result is
|
|
// `null` promise is not resolved yet.
|
|
var result = null;
|
|
|
|
var deferred = {
|
|
promise: {
|
|
then: function then(onFulfill, onError) {
|
|
var deferred = defer();
|
|
|
|
// Decorate `onFulfill` / `onError` handlers with `attempt`, that
|
|
// way if wrapped handler throws exception decorator will catch and
|
|
// return promise rejected with it, which will cause rejection of
|
|
// `deferred.promise`. If handler is missing, substitute it with an
|
|
// utility function that takes one argument and returns promise
|
|
// fulfilled / rejected with it. This takes care of propagation
|
|
// through the rest of the promise chain.
|
|
onFulfill = onFulfill ? attempt(onFulfill) : fulfilled;
|
|
onError = onError ? attempt(onError) : rejected;
|
|
|
|
// Create a pair of observers that invoke given handlers & propagate
|
|
// results to `deferred.promise`.
|
|
function resolveDeferred(value) { deferred.resolve(onFulfill(value)); }
|
|
function rejectDeferred(reason) { deferred.resolve(onError(reason)); }
|
|
|
|
// If enclosed promise (`this.promise`) observers queue is still alive
|
|
// enqueue a new observer pair into it. Note that this does not
|
|
// necessary means that promise is pending, it may already be resolved,
|
|
// but we still have to queue observers to guarantee an order of
|
|
// propagation.
|
|
if (observers) {
|
|
observers.push({ resolve: resolveDeferred, reject: rejectDeferred });
|
|
}
|
|
// Otherwise just forward observer pair right to a `result` promise.
|
|
else {
|
|
result.then(resolveDeferred, rejectDeferred);
|
|
}
|
|
|
|
return deferred.promise;
|
|
}
|
|
},
|
|
/**
|
|
* Resolves associated `promise` to a given `value`, unless it's already
|
|
* resolved or rejected. Note that resolved promise is not necessary a
|
|
* successfully fulfilled. Promise may be resolved with a promise `value`
|
|
* in which case `value` promise's fulfillment / rejection will propagate
|
|
* up to a promise resolved with `value`.
|
|
*/
|
|
resolve: function resolve(value) {
|
|
if (!result) {
|
|
// Store resolution `value` in a `result` as a promise, so that all
|
|
// the subsequent handlers can be simply forwarded to it. Since
|
|
// `result` will be a promise all the value / error propagation will
|
|
// be uniformly taken care of.
|
|
result = isPromise(value) ? value : fulfilled(value);
|
|
|
|
// Forward already registered observers to a `result` promise in the
|
|
// order they were registered. Note that we intentionally dequeue
|
|
// observer at a time until queue is exhausted. This makes sure that
|
|
// handlers registered as side effect of observer forwarding are
|
|
// queued instead of being invoked immediately, guaranteeing FIFO
|
|
// order.
|
|
while (observers.length) {
|
|
var observer = observers.shift();
|
|
result.then(observer.resolve, observer.reject);
|
|
}
|
|
|
|
// Once `observers` queue is exhausted we `null`-ify it, so that
|
|
// new handlers are forwarded straight to the `result`.
|
|
observers = null;
|
|
}
|
|
},
|
|
/**
|
|
* Rejects associated `promise` with a given `reason`, unless it's already
|
|
* resolved / rejected. This is just a (better performing) convenience
|
|
* shortcut for `deferred.resolve(reject(reason))`.
|
|
*/
|
|
reject: function reject(reason) {
|
|
// Note that if promise is resolved that does not necessary means that it
|
|
// is successfully fulfilled. Resolution value may be a promise in which
|
|
// case its result propagates. In other words if promise `a` is resolved
|
|
// with promise `b`, `a` is either fulfilled or rejected depending
|
|
// on weather `b` is fulfilled or rejected. Here `deferred.promise` is
|
|
// resolved with a promise pre-rejected with a given `reason`, there for
|
|
// `deferred.promise` is rejected with a given `reason`. This may feel
|
|
// little awkward first, but doing it this way greatly simplifies
|
|
// propagation through promise chains.
|
|
deferred.resolve(rejected(reason));
|
|
}
|
|
};
|
|
|
|
return deferred;
|
|
}
|
|
exports.defer = defer;
|
|
|
|
/**
|
|
* Returns a promise resolved to a given `value`.
|
|
*/
|
|
function resolve(value) {
|
|
var deferred = defer();
|
|
deferred.resolve(value);
|
|
return deferred.promise;
|
|
}
|
|
exports.resolve = resolve;
|
|
|
|
/**
|
|
* Returns a promise rejected with a given `reason`.
|
|
*/
|
|
function reject(reason) {
|
|
var deferred = defer();
|
|
deferred.reject(reason);
|
|
return deferred.promise;
|
|
}
|
|
exports.reject = reject;
|
|
|
|
});
|