mirror of
https://gitlab.winehq.org/wine/wine-gecko.git
synced 2024-09-13 09:24:08 -07:00
216 lines
5.2 KiB
JavaScript
216 lines
5.2 KiB
JavaScript
|
/*
|
||
|
* Copyright 2009-2011 Mozilla Foundation and contributors
|
||
|
* Licensed under the New BSD license. See LICENSE.txt or:
|
||
|
* http://opensource.org/licenses/BSD-3-Clause
|
||
|
*/
|
||
|
|
||
|
|
||
|
var EXPORTED_SYMBOLS = [ "Promise" ];
|
||
|
|
||
|
/**
|
||
|
* Create an unfulfilled promise
|
||
|
* @constructor
|
||
|
*/
|
||
|
function Promise() {
|
||
|
this._status = Promise.PENDING;
|
||
|
this._value = undefined;
|
||
|
this._onSuccessHandlers = [];
|
||
|
this._onErrorHandlers = [];
|
||
|
|
||
|
// Debugging help
|
||
|
this._id = Promise._nextId++;
|
||
|
Promise._outstanding[this._id] = this;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* We give promises and ID so we can track which are outstanding
|
||
|
*/
|
||
|
Promise._nextId = 0;
|
||
|
|
||
|
/**
|
||
|
* Outstanding promises. Handy list for debugging only
|
||
|
*/
|
||
|
Promise._outstanding = [];
|
||
|
|
||
|
/**
|
||
|
* Recently resolved promises. Also for debugging only
|
||
|
*/
|
||
|
Promise._recent = [];
|
||
|
|
||
|
/**
|
||
|
* A promise can be in one of 2 states.
|
||
|
* The ERROR and SUCCESS states are terminal, the PENDING state is the only
|
||
|
* start state.
|
||
|
*/
|
||
|
Promise.ERROR = -1;
|
||
|
Promise.PENDING = 0;
|
||
|
Promise.SUCCESS = 1;
|
||
|
|
||
|
/**
|
||
|
* Yeay for RTTI
|
||
|
*/
|
||
|
Promise.prototype.isPromise = true;
|
||
|
|
||
|
/**
|
||
|
* Have we either been resolve()ed or reject()ed?
|
||
|
*/
|
||
|
Promise.prototype.isComplete = function() {
|
||
|
return this._status != Promise.PENDING;
|
||
|
};
|
||
|
|
||
|
/**
|
||
|
* Have we resolve()ed?
|
||
|
*/
|
||
|
Promise.prototype.isResolved = function() {
|
||
|
return this._status == Promise.SUCCESS;
|
||
|
};
|
||
|
|
||
|
/**
|
||
|
* Have we reject()ed?
|
||
|
*/
|
||
|
Promise.prototype.isRejected = function() {
|
||
|
return this._status == Promise.ERROR;
|
||
|
};
|
||
|
|
||
|
/**
|
||
|
* Take the specified action of fulfillment of a promise, and (optionally)
|
||
|
* a different action on promise rejection
|
||
|
*/
|
||
|
Promise.prototype.then = function(onSuccess, onError) {
|
||
|
if (typeof onSuccess === 'function') {
|
||
|
if (this._status === Promise.SUCCESS) {
|
||
|
onSuccess.call(null, this._value);
|
||
|
}
|
||
|
else if (this._status === Promise.PENDING) {
|
||
|
this._onSuccessHandlers.push(onSuccess);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (typeof onError === 'function') {
|
||
|
if (this._status === Promise.ERROR) {
|
||
|
onError.call(null, this._value);
|
||
|
}
|
||
|
else if (this._status === Promise.PENDING) {
|
||
|
this._onErrorHandlers.push(onError);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return this;
|
||
|
};
|
||
|
|
||
|
/**
|
||
|
* Like then() except that rather than returning <tt>this</tt> we return
|
||
|
* a promise which resolves when the original promise resolves
|
||
|
*/
|
||
|
Promise.prototype.chainPromise = function(onSuccess) {
|
||
|
var chain = new Promise();
|
||
|
chain._chainedFrom = this;
|
||
|
this.then(function(data) {
|
||
|
try {
|
||
|
chain.resolve(onSuccess(data));
|
||
|
}
|
||
|
catch (ex) {
|
||
|
chain.reject(ex);
|
||
|
}
|
||
|
}, function(ex) {
|
||
|
chain.reject(ex);
|
||
|
});
|
||
|
return chain;
|
||
|
};
|
||
|
|
||
|
/**
|
||
|
* Supply the fulfillment of a promise
|
||
|
*/
|
||
|
Promise.prototype.resolve = function(data) {
|
||
|
return this._complete(this._onSuccessHandlers,
|
||
|
Promise.SUCCESS, data, 'resolve');
|
||
|
};
|
||
|
|
||
|
/**
|
||
|
* Renege on a promise
|
||
|
*/
|
||
|
Promise.prototype.reject = function(data) {
|
||
|
return this._complete(this._onErrorHandlers, Promise.ERROR, data, 'reject');
|
||
|
};
|
||
|
|
||
|
/**
|
||
|
* Internal method to be called on resolve() or reject()
|
||
|
* @private
|
||
|
*/
|
||
|
Promise.prototype._complete = function(list, status, data, name) {
|
||
|
// Complain if we've already been completed
|
||
|
if (this._status != Promise.PENDING) {
|
||
|
if (typeof 'console' === 'object') {
|
||
|
console.error('Promise complete. Attempted ' + name + '() with ', data);
|
||
|
console.error('Prev status = ', this._status, ', value = ', this._value);
|
||
|
}
|
||
|
throw new Error('Promise already complete');
|
||
|
}
|
||
|
|
||
|
this._status = status;
|
||
|
this._value = data;
|
||
|
|
||
|
// Call all the handlers, and then delete them
|
||
|
list.forEach(function(handler) {
|
||
|
handler.call(null, this._value);
|
||
|
}, this);
|
||
|
delete this._onSuccessHandlers;
|
||
|
delete this._onErrorHandlers;
|
||
|
|
||
|
// Remove the given {promise} from the _outstanding list, and add it to the
|
||
|
// _recent list, pruning more than 20 recent promises from that list
|
||
|
delete Promise._outstanding[this._id];
|
||
|
// The original code includes this very useful debugging aid, however there
|
||
|
// is concern that it will create a memory leak, so we leave it out here.
|
||
|
/*
|
||
|
Promise._recent.push(this);
|
||
|
while (Promise._recent.length > 20) {
|
||
|
Promise._recent.shift();
|
||
|
}
|
||
|
*/
|
||
|
|
||
|
return this;
|
||
|
};
|
||
|
|
||
|
/**
|
||
|
* Takes an array of promises and returns a promise that that is fulfilled once
|
||
|
* all the promises in the array are fulfilled
|
||
|
* @param promiseList The array of promises
|
||
|
* @return the promise that is fulfilled when all the array is fulfilled
|
||
|
*/
|
||
|
Promise.group = function(promiseList) {
|
||
|
if (!Array.isArray(promiseList)) {
|
||
|
promiseList = Array.prototype.slice.call(arguments);
|
||
|
}
|
||
|
|
||
|
// If the original array has nothing in it, return now to avoid waiting
|
||
|
if (promiseList.length === 0) {
|
||
|
return new Promise().resolve([]);
|
||
|
}
|
||
|
|
||
|
var groupPromise = new Promise();
|
||
|
var results = [];
|
||
|
var fulfilled = 0;
|
||
|
|
||
|
var onSuccessFactory = function(index) {
|
||
|
return function(data) {
|
||
|
results[index] = data;
|
||
|
fulfilled++;
|
||
|
// If the group has already failed, silently drop extra results
|
||
|
if (groupPromise._status !== Promise.ERROR) {
|
||
|
if (fulfilled === promiseList.length) {
|
||
|
groupPromise.resolve(results);
|
||
|
}
|
||
|
}
|
||
|
};
|
||
|
};
|
||
|
|
||
|
promiseList.forEach(function(promise, index) {
|
||
|
var onSuccess = onSuccessFactory(index);
|
||
|
var onError = groupPromise.reject.bind(groupPromise);
|
||
|
promise.then(onSuccess, onError);
|
||
|
});
|
||
|
|
||
|
return groupPromise;
|
||
|
};
|