TimeTrex/interface/html5/global/TTPromise.js

197 lines
7.1 KiB
JavaScript
Raw Normal View History

2022-12-13 07:10:06 +01:00
export var TTPromise = {
promises: {},
add: function( category, key ) {
if ( !this.promises[category] || !this.promises[category][key] || ( this.promises[category][key].state() != 'pending' ) ) {
Debug.Text( 'Promise: add: ' + category + '|' + key, 'TTPromise.js', 'TTPromise.js', 'add', 11 );
if ( typeof this.promises[category] == 'undefined' ) {
this.promises[category] = {};
}
this.promises[category][key] = $.Deferred();
this.promises[category][key].extra_data = { category: category, key: key };
} else {
Debug.Text( 'Promise: already exists: ' + category + '|' + key, 'TTPromise.js', 'TTPromise.js', 'add', 11 );
}
},
resolve: function( category, key ) {
if ( this.promises && this.promises[category] && this.promises[category][key] ) {
Debug.Text( 'Promise: resolved: ' + category + '|' + key, 'TTPromise.js', 'TTPromise.js', 'resolve', 11 );
this.promises[category][key].resolve( this.promises[category][key].extra_data );
}
},
reject: function( category, key ) {
if ( this.promises && this.promises[category] && this.promises[category][key] ) {
Debug.Text( 'Promise: rejected: ' + category + '|' + key, 'TTPromise.js', 'TTPromise.js', 'reject', 11 );
this.promises[category][key].reject( this.promises[category][key].extra_data );
}
},
/**
* Wait for all or specific category/key
*
* In the case of waiting on all promises, error_callback is triggered for each individual error (rejection), and success callback is called when all promises are resolved (either success or failure)
* so when waiting for all promises, most times you don't want a failure callback because both "success" and "failure" can happen on the same wait.
*
* The function call should look like:
* TTPromise.wait(null,null,function(){
* //do stuff on success
* });
*
* @param category
* @param key
* @param success_callback
* @param error_callback
*/
wait: function( category, key, success_callback, error_callback ) {
Debug.Arr( arguments, 'Promise: wait(' + category + '|' + key + ').', 'TTPromise.js', 'TTPromise.js', 'wait', 11 );
if ( typeof success_callback != 'function' ) {
success_callback = function() {
Global.setUIInitComplete();
Debug.Text( 'Promise: resolved with default callback.', 'TTPromise.js', 'TTPromise.js', 'wait', 11 );
};
}
if ( typeof error_callback != 'function' ) {
error_callback = function() {
Global.setUIInitComplete();
Debug.Text( 'Promise failed with default callback.', 'TTPromise.js', 'TTPromise.js', 'wait', 1 );
};
}
if ( Object.keys( TTPromise.promises ).length > 0 ) {
var onComplete = function() {
for ( var i = 0; i < arguments.length; i++ ) {
//numerically indexed arguments come back from Promise resolution arguments.
var obj = arguments[i];
var category = obj.category;
var key = obj.key;
var wait_category = obj.wait_arguments.category;
var wait_key = obj.wait_arguments.key;
TTPromise.clearCompletedPromise( category, key );
}
if ( !wait_category || TTPromise.filterPromiseArray( wait_category, wait_key ).length == 0 ) {
Debug.Text( 'Promise: success callback', 'TTPromise.js', 'TTPromise.js', 'wait', 11 );
success_callback( true );
} else {
Debug.Text( 'Promise: waiting again ' + category + '|' + key, 'TTPromise.js', 'TTPromise.js', 'wait', 11 );
TTPromise.wait( category, key, success_callback, error_callback );
}
return true;
};
var onError = function() {
//When one promise fails in a category, we need to call the error_callback if waiting on that category, and *never* call the success callback.
Debug.Text( 'Promise: ERROR callback', 'TTPromise.js', 'TTPromise.js', 'wait', 11 );
for ( var i = 0; i < arguments.length; i++ ) {
//numerically indexed arguments come back from Promise resolution arguments.
var obj = arguments[i];
var category = obj.category;
var key = obj.key;
var wait_category = obj.wait_arguments.category;
var wait_key = obj.wait_arguments.key;
TTPromise.clearCompletedPromise( category, key );
}
//Different than success because we always want to call the error_callback immediately, as the category can never be success after a single error.
if ( typeof error_callback != 'undefined' ) {
error_callback( false );
}
//Don't wait again, as that will cause success/error callbacks to be triggered multiple times and could cause an infinite loop too.
//TTPromise.wait( wait_category, wait_key, success_callback, error_callback );
return true;
};
var pending_promises = this.filterPromiseArray( category, key );
$.when.apply( $, pending_promises ).pipe( onComplete, onError );
} else {
success_callback( true );
}
},
filterPromiseArray: function( category, key ) {
var retval = [];
for ( var c in this.promises ) {
if ( !category || c == category ) {
//Debug.Text('Promise: processing category: '+ c, 'TTPromise.js', 'TTPromise.js', 'pending_to_waiting', 11);
for ( var k in this.promises[c] ) {
if ( !key || k == key ) {
//Debug.Text('Promise: processing key: '+ c+'|'+k, 'TTPromise.js', 'TTPromise.js', 'pending_to_waiting', 11);
this.promises[c][k].extra_data.wait_arguments = { category: category, key: key };
retval.push( this.promises[c][k] );
} else {
//Debug.Text('Promise: ignoring key: '+ k, 'TTPromise.js', 'TTPromise.js', 'pending_to_waiting', 11);
}
}
} else {
//Debug.Text('Promise: ignoring category: '+ c, 'TTPromise.js', 'TTPromise.js', 'pending_to_waiting', 11);
}
}
return retval;
},
clearCompletedPromise: function( category, key ) {
if ( this.promises && category && key ) {
if ( this.promises[category] && this.promises[category][key] ) {
this.promises[category][key] = false;
delete this.promises[category][key];
Debug.Text( 'Promise: clear category: ' + category + '|' + key, 'TTPromise.js', 'TTPromise.js', 'init', 11 );
}
if ( this.promises[category] && Object.keys( this.promises[category] ).length == 0 ) {
this.promises[category] = false;
Debug.Text( 'Promise: clear category: ' + category, 'TTPromise.js', 'TTPromise.js', 'init', 11 );
delete this.promises[category];
}
return true;
}
return false;
},
//clear existing promises (mostly for testing and first add)
clearAllPromises: function() {
Debug.Text( 'Promise: clear all promises.', 'TTPromise.js', 'TTPromise.js', 'init', 11 );
this.promises = {};
},
isPendingPromises: function( category, key ) {
var p = TTPromise.filterPromiseArray( category, key );
var pending_count = 0;
for ( var n in p ) {
if ( p[n] && p[n].state() == 'pending' ) {
console.debug( 'Category: ' + p[n].extra_data.category + ' Key: ' + p[n].extra_data.key + ' State: ' + p[n].state() );
pending_count++;
}
}
if ( pending_count > 0 ) {
return true;
}
return false;
},
debugPromises: function( category, key ) {
var p = TTPromise.filterPromiseArray( category, key );
for ( var n in p ) {
console.debug( 'Category: ' + p[n].extra_data.category + ' Key: ' + p[n].extra_data.key + ' State: ' + p[n].state() );
}
return true;
}
};