(function() {
/**
* use native implementation if available
* in some older UI5 systems is a Promise implementation for IE that is incomplete
* therefore check the whole interface before using it; otherwise use our solution
* @return {Promise|undefined}
*/
if ((() => {
const {Promise} = window;
if (typeof Promise?.all === 'function' &&
typeof Promise?.resolve === 'function' &&
typeof Promise?.reject === 'function' &&
typeof Promise?.prototype?.then === 'function' &&
typeof Promise?.prototype?.catch === 'function' &&
typeof Promise?.prototype?.finally === 'function') // XRAY-2865
{
return Help4.Promise = Promise;
}
})()) return;
const setImmediate = window.setImmediate || (executor => void window.setTimeout(executor, 0));
/**
* @typedef {Object} Help4.Promise.Callback
* @property {Function|null} onResolve
* @property {Function|null} onReject
* @property {Help4.Promise} promise
*/
/**
* @typedef {Function} Help4.Promise.Executor
* @param {Function} resolve
* @param {Function} reject
*/
/**
* @typedef {{pending: 'pending', fulfilled: 'fulfilled', promise: 'promise', error: 'error'}} Help4.Promise.STATE
*/
const STATE = {
pending: 'pending',
fulfilled: 'fulfilled',
error: 'error',
promise: 'promise'
};
/** @augments Promise */
Help4.Promise = class {
/**
* @param {Help4.Promise.Executor} executor
*/
constructor(executor) {
this._state = STATE.pending;
this._value = undefined;
this._callbacks = [];
_execute.call(this, executor);
}
/**
* @param {*} [value = undefined]
* @return {Help4.Promise<*>}
*/
static resolve(value = undefined) {
const {Promise} = Help4;
return value instanceof Promise ? value : new Promise(resolve => void resolve(value));
}
/**
* @param {*} [reason = undefined]
* @return {Help4.Promise<*>}
*/
static reject(reason = undefined) {
return new Help4.Promise((resolve, reject) => void reject(reason));
}
/**
* @param {Array<Help4.Promise<*>>} promises
* @return {Help4.Promise<*>}
*/
static all(promises) {
return new Help4.Promise((resolve, reject) => {
let count = promises.length;
const results = new Array(count);
if (!count) return resolve(results);
const execute = (idx, promise) => {
try {
promise
.then(value => {
results[idx] = value;
!(--count) && resolve(results);
})
.catch(reject);
} catch (e) {
reject(e);
}
}
for (const [i, promise] of Help4.arrayEntries(promises)) {
if (promise instanceof Help4.Promise) {
execute(i, promise);
} else {
results[i] = promise;
!(--count) && resolve(results);
}
}
});
}
/**
* @member
*/
destroy() {
delete this._state;
delete this._value;
delete this._callbacks;
}
/**
* @param {Function} onResolve
* @param {Function} [onReject]
* @return {Help4.Promise<*>}
*/
then(onResolve, onReject = null) {
const promise = new Help4.Promise(() => {});
_callback.call(this, {
onResolve: onResolve || null,
onReject: onReject || null,
promise
});
return promise;
}
/**
* @param {Function} onReject
* @return {Help4.Promise<*>}
*/
catch(onReject) {
return this.then(null, onReject);
}
/**
* @param {Function} onFinally
* @return {Help4.Promise<*>}
*/
finally(onFinally) {
const onResolve = value => {
return Help4.Promise.resolve(onFinally())
.then(() => value);
}
const onReject = reason => {
return Help4.Promise.resolve(onFinally())
.then(() => Help4.Promise.reject(reason));
}
return this.then(onResolve, onReject);
}
}
/**
* @param {Help4.Promise.Executor} executor
* @memberof Help4.Promise#
* @private
*/
function _execute(executor) {
let done = false;
const onResolve = value => void (!done && (done = true) && _resolve.call(this, value));
const onReject = reason => void (!done && (done = true) && _reject.call(this, reason));
try {
executor(onResolve, onReject);
} catch(e) {
onReject(e);
}
}
/**
* @param {*} value
* @memberof Help4.Promise#
* @private
*/
function _resolve(value) {
try {
_setValue.call(this, value instanceof Help4.Promise ? STATE.promise : STATE.fulfilled, value);
} catch(e) {
_reject.call(this, e);
}
}
/**
* @param {*} reason
* @memberof Help4.Promise#
* @private
*/
function _reject(reason) {
_setValue.call(this, STATE.error, reason);
}
/**
* @param {Help4.Promise.STATE} state
* @param {*} value
* @memberof Help4.Promise#
* @private
*/
function _setValue(state, value) {
this._state = state;
this._value = value;
/** @type {Help4.Promise.Callback} */
let c;
while (c = this._callbacks.shift()) {
_callback.call(this, c);
}
this._callbacks = null;
}
/**
*
* @param {Help4.Promise.Callback} callback
* @memberof Help4.Promise#
* @private
*/
function _callback(callback) {
let scope = this;
while (scope._state === STATE.promise) scope = scope._value;
if (scope._state === STATE.pending) {
scope._callbacks.push(callback);
} else {
setImmediate(() => _forwardExecute.call(scope, callback));
}
}
/**
* @param {Help4.Promise.Callback} callback
* @memberof Help4.Promise#
* @private
*/
function _forwardExecute(callback) {
const fun = this._state === STATE.fulfilled ? callback.onResolve : callback.onReject;
if (fun) {
let result;
try {
result = fun(this._value);
} catch(e) {
_reject.call(callback.promise, e);
return;
}
_resolve.call(callback.promise, result);
} else {
this._state === STATE.fulfilled
? _resolve.call(callback.promise, this._value)
: _reject.call(callback.promise, this._value);
}
}
})();