| 
									
										
										
										
											2020-11-07 02:49:32 +03:00
										 |  |  | /********************************************************************** | 
					
						
							|  |  |  | *  | 
					
						
							|  |  |  | * | 
					
						
							|  |  |  | * | 
					
						
							| 
									
										
										
										
											2020-11-11 17:39:47 +03:00
										 |  |  | * | 
					
						
							|  |  |  | * XXX would be helpful to define a task api... | 
					
						
							| 
									
										
										
										
											2020-11-11 17:47:32 +03:00
										 |  |  | * 		task('abort') vs. task.abort(), task state,  ...etc. | 
					
						
							|  |  |  | * 	then define Task and TaskQueue(Queue) and extended api to: | 
					
						
							|  |  |  | * 		- task state introspection | 
					
						
							|  |  |  | * 		- stop/resume tasks (or task queue?) | 
					
						
							|  |  |  | * 		- serialize tasks | 
					
						
							|  |  |  | * 		- ... | 
					
						
							|  |  |  | * 	would be nice to make the task just a slightly extended or better | 
					
						
							|  |  |  | * 	defined function/generator, ideally to make them interchangable... | 
					
						
							| 
									
										
										
										
											2020-11-11 17:39:47 +03:00
										 |  |  | * | 
					
						
							| 
									
										
										
										
											2020-11-07 02:49:32 +03:00
										 |  |  | **********************************************/  /* c8 ignore next 2 */ | 
					
						
							|  |  |  | ((typeof define)[0]=='u'?function(f){module.exports=f(require)}:define) | 
					
						
							|  |  |  | (function(require){ var module={} // make module AMD/node compatible...
 | 
					
						
							|  |  |  | /*********************************************************************/ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | var object = require('ig-object') | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-11-16 02:38:19 +03:00
										 |  |  | require('./Array') | 
					
						
							| 
									
										
										
										
											2020-11-24 05:48:54 +03:00
										 |  |  | require('./Promise') | 
					
						
							| 
									
										
										
										
											2020-11-16 02:38:19 +03:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-11-15 00:24:22 +03:00
										 |  |  | var events = require('./event') | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-11-07 02:49:32 +03:00
										 |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /*********************************************************************/ | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-11-23 02:59:18 +03:00
										 |  |  | module.STOP = object.STOP | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-12-17 18:55:28 +03:00
										 |  |  | module.SKIP = {doc: 'skip queue item',} | 
					
						
							| 
									
										
										
										
											2020-11-23 02:59:18 +03:00
										 |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | //---------------------------------------------------------------------
 | 
					
						
							| 
									
										
										
										
											2020-11-25 04:04:44 +03:00
										 |  |  | // Queue...
 | 
					
						
							|  |  |  | //
 | 
					
						
							|  |  |  | // A means to manage execution of large-ish number of small tasks...
 | 
					
						
							|  |  |  | //
 | 
					
						
							| 
									
										
										
										
											2020-11-30 19:20:33 +03:00
										 |  |  | // A queue is a list of async functions that get executed in order and 
 | 
					
						
							|  |  |  | // not more than .pool_size can run at a time, i.e. new tasks get 
 | 
					
						
							|  |  |  | // started only only when tasks in the running pool either finish or 
 | 
					
						
							|  |  |  | // release their spot in the pool.
 | 
					
						
							|  |  |  | //
 | 
					
						
							| 
									
										
										
										
											2020-12-21 05:23:16 +03:00
										 |  |  | // XXX would be nice to:
 | 
					
						
							|  |  |  | // 		- nest queues -- DONE
 | 
					
						
							|  |  |  | // 		- chain queues
 | 
					
						
							|  |  |  | // 		- weave queues -- a-la generator's .map(..) etc.
 | 
					
						
							| 
									
										
										
										
											2020-11-25 04:04:44 +03:00
										 |  |  | // XXX need to configure to run a specific amount of jobs on each start...
 | 
					
						
							| 
									
										
										
										
											2020-12-02 06:28:11 +03:00
										 |  |  | // XXX document the Queue({handler: e => e*e}, 1,2,3,4) use-case...
 | 
					
						
							|  |  |  | // 		...this is essentially a .map(..) variant...
 | 
					
						
							| 
									
										
										
										
											2020-11-07 02:49:32 +03:00
										 |  |  | var Queue = | 
					
						
							| 
									
										
										
										
											2020-11-24 05:48:54 +03:00
										 |  |  | module.Queue =  | 
					
						
							|  |  |  | object.Constructor('Queue', Array, { | 
					
						
							| 
									
										
										
										
											2020-11-07 05:36:20 +03:00
										 |  |  | 	// create a running queue...
 | 
					
						
							| 
									
										
										
										
											2020-12-08 01:53:40 +03:00
										 |  |  | 	//
 | 
					
						
							| 
									
										
										
										
											2020-11-23 03:07:41 +03:00
										 |  |  | 	runTasks: function(...tasks){ | 
					
						
							| 
									
										
										
										
											2020-12-08 01:53:40 +03:00
										 |  |  | 		if(typeof(tasks[0]) != 'function' | 
					
						
							|  |  |  | 				&& !(tasks[0] instanceof Queue) | 
					
						
							|  |  |  | 				&& typeof(tasks[0].finally) != 'function'){ | 
					
						
							|  |  |  | 			var [options, ...tasks] = arguments } | 
					
						
							|  |  |  | 		return this( | 
					
						
							|  |  |  | 			Object.assign({}, | 
					
						
							|  |  |  | 				options || {}, | 
					
						
							|  |  |  | 				{ state: 'running' }),  | 
					
						
							|  |  |  | 			...tasks) }, | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// Create a handler queue...
 | 
					
						
							|  |  |  | 	//
 | 
					
						
							|  |  |  | 	// 	Queue.handle(func, ...data)
 | 
					
						
							|  |  |  | 	// 	Queue.handle(options, func, ...data)
 | 
					
						
							|  |  |  | 	// 		-> queue
 | 
					
						
							|  |  |  | 	//
 | 
					
						
							|  |  |  | 	// NOTE: func(..) should be compatible with .handler(..) instance method...
 | 
					
						
							|  |  |  | 	// NOTE: this is a shorthand for:
 | 
					
						
							|  |  |  | 	// 			Queue({handler: func, ...}, ...data)
 | 
					
						
							|  |  |  | 	handle: function(handler, ...data){ | 
					
						
							|  |  |  | 		// NOTE: this is a simpler test than in .runTasks(..) above because
 | 
					
						
							|  |  |  | 		// 		here we are expecting a function as the first arg in the 
 | 
					
						
							|  |  |  | 		// 		general case while above a non-task is the exception..
 | 
					
						
							|  |  |  | 		if(typeof(handler) != 'function'){ | 
					
						
							|  |  |  | 			var [options, handler, ...data] = arguments } | 
					
						
							|  |  |  | 		return this( | 
					
						
							|  |  |  | 			Object.assign({}, | 
					
						
							|  |  |  | 				options || {}, | 
					
						
							|  |  |  | 				{handler}),  | 
					
						
							|  |  |  | 			...data) }, | 
					
						
							| 
									
										
										
										
											2020-11-07 02:49:32 +03:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-11-19 02:24:09 +03:00
										 |  |  | }, events.EventMixin('flat', { | 
					
						
							| 
									
										
										
										
											2020-12-03 05:17:59 +03:00
										 |  |  | 	// Config...
 | 
					
						
							| 
									
										
										
										
											2020-11-07 05:36:20 +03:00
										 |  |  | 	//
 | 
					
						
							| 
									
										
										
										
											2020-12-02 21:02:38 +03:00
										 |  |  | 	// Number of tasks to be running at the same time...
 | 
					
						
							| 
									
										
										
										
											2020-11-07 02:49:32 +03:00
										 |  |  | 	pool_size: 8, | 
					
						
							| 
									
										
										
										
											2020-11-07 05:36:20 +03:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-12-03 05:17:59 +03:00
										 |  |  | 	// Number of tasks to run before letting go of the exec frame...
 | 
					
						
							| 
									
										
										
										
											2020-12-02 21:02:38 +03:00
										 |  |  | 	pause_after_sync: 4, | 
					
						
							| 
									
										
										
										
											2020-12-02 19:53:39 +03:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-12-16 22:30:04 +03:00
										 |  |  | 	// XXX
 | 
					
						
							|  |  |  | 	auto_start: false, | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-12-03 05:17:59 +03:00
										 |  |  | 	// Start synchronously...
 | 
					
						
							|  |  |  | 	//
 | 
					
						
							|  |  |  | 	// NOTE: this affects the start only, all other timeouts apply as-is... 
 | 
					
						
							|  |  |  | 	sync_start: false, | 
					
						
							| 
									
										
										
										
											2020-11-07 05:36:20 +03:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-12-02 01:18:48 +03:00
										 |  |  | 	catch_errors: true, | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-12-03 05:17:59 +03:00
										 |  |  | 	// If true, stop after queue is depleted...
 | 
					
						
							|  |  |  | 	auto_stop: false, | 
					
						
							| 
									
										
										
										
											2020-12-02 19:53:39 +03:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-12-15 04:02:38 +03:00
										 |  |  | 	// NOTE: for long running queues this may get quite big...
 | 
					
						
							|  |  |  | 	collect_results: true, | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-12-03 05:17:59 +03:00
										 |  |  | 	// Sub-queue handling mode...
 | 
					
						
							| 
									
										
										
										
											2020-11-23 02:59:18 +03:00
										 |  |  | 	//
 | 
					
						
							|  |  |  | 	// This can be:
 | 
					
						
							|  |  |  | 	// 	'wait'		- wait fot the sun-queue to stop
 | 
					
						
							|  |  |  | 	// 	'unwind'	- run sub-task and requeue parent
 | 
					
						
							|  |  |  | 	//
 | 
					
						
							|  |  |  | 	// XXX do we need this???
 | 
					
						
							| 
									
										
										
										
											2020-12-03 05:17:59 +03:00
										 |  |  | 	// XXX should the nested queue decide??? ...how???
 | 
					
						
							| 
									
										
										
										
											2020-11-23 02:59:18 +03:00
										 |  |  | 	sub_queue: 'unwind', | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-12-03 05:17:59 +03:00
										 |  |  | 	// If true only add unique items to queue...
 | 
					
						
							|  |  |  | 	// XXX not implemented yet... 
 | 
					
						
							|  |  |  | 	// 		...how should this be done?
 | 
					
						
							|  |  |  | 	// 			- keep a set of seen elements and check against it?
 | 
					
						
							|  |  |  | 	// 			- check against the queue contents?
 | 
					
						
							|  |  |  | 	unique_items: false, | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// Timeouts...
 | 
					
						
							|  |  |  | 	//
 | 
					
						
							|  |  |  | 	// Time to wait when pool is full...
 | 
					
						
							|  |  |  | 	// if 'auto', wait the average task time * .busy_timeout_scale.
 | 
					
						
							|  |  |  | 	// XXX revise defaults...
 | 
					
						
							|  |  |  | 	busy_timeout: 50, | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	//busy_timeout: 'auto',
 | 
					
						
							|  |  |  | 	busy_timeout_scale: 5, | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// Time to wait between checks for new tasks in an empty queue...
 | 
					
						
							|  |  |  | 	poling_timeout: 200, | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// Time to pause after a set of .pause_after_sync sync tasks...
 | 
					
						
							|  |  |  | 	pause_timeout: 0, | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// Runtime statistics...
 | 
					
						
							|  |  |  | 	//
 | 
					
						
							|  |  |  | 	// To disable set to false
 | 
					
						
							|  |  |  | 	//
 | 
					
						
							|  |  |  | 	// NOTE: this, if true, will get replaced with the stats...
 | 
					
						
							|  |  |  | 	runtime_stats: true, | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-11-23 02:59:18 +03:00
										 |  |  | 	//
 | 
					
						
							|  |  |  | 	// This can be:
 | 
					
						
							|  |  |  | 	// 		'running'
 | 
					
						
							|  |  |  | 	// 		'stopped'
 | 
					
						
							|  |  |  | 	//
 | 
					
						
							| 
									
										
										
										
											2020-11-07 02:49:32 +03:00
										 |  |  | 	__state: null, | 
					
						
							|  |  |  | 	get state(){ | 
					
						
							|  |  |  | 		return this.__state  | 
					
						
							|  |  |  | 			|| 'stopped' }, | 
					
						
							|  |  |  | 	set state(value){ | 
					
						
							| 
									
										
										
										
											2020-11-07 05:36:20 +03:00
										 |  |  | 		if(value == 'running'){ | 
					
						
							|  |  |  | 			this.start() | 
					
						
							|  |  |  | 		} else if(value == 'stopped'){ | 
					
						
							| 
									
										
										
										
											2020-12-16 03:50:59 +03:00
										 |  |  | 			this.stop()  | 
					
						
							|  |  |  | 		} else if(value == 'aborted'){ | 
					
						
							|  |  |  | 			this.abort() } }, | 
					
						
							| 
									
										
										
										
											2020-11-07 02:49:32 +03:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-11-07 05:36:20 +03:00
										 |  |  | 	// events/actions - state transitions...
 | 
					
						
							|  |  |  | 	//
 | 
					
						
							| 
									
										
										
										
											2020-12-02 19:53:39 +03:00
										 |  |  | 	// NOTE: to start synchronously call .start(true), this will not 
 | 
					
						
							|  |  |  | 	// 		affect further operation...
 | 
					
						
							| 
									
										
										
										
											2020-12-17 18:55:28 +03:00
										 |  |  | 	//
 | 
					
						
							| 
									
										
										
										
											2020-12-02 05:29:38 +03:00
										 |  |  | 	// XXX would be nice to run a specific number of tasks and stop...
 | 
					
						
							| 
									
										
										
										
											2020-12-16 22:30:04 +03:00
										 |  |  | 	// XXX might be a good idea to let the user set .__wait_for_items...
 | 
					
						
							| 
									
										
										
										
											2020-12-17 18:55:28 +03:00
										 |  |  | 	// XXX should we wait for items on empty?
 | 
					
						
							| 
									
										
										
										
											2020-12-16 22:30:04 +03:00
										 |  |  | 	__wait_for_items: null, | 
					
						
							| 
									
										
										
										
											2020-12-02 19:53:39 +03:00
										 |  |  | 	start: events.Event('start', function(handle, sync){ | 
					
						
							| 
									
										
										
										
											2020-12-16 22:30:04 +03:00
										 |  |  | 		// first start -- wait for items...
 | 
					
						
							|  |  |  | 		if('__state' in this){ | 
					
						
							|  |  |  | 			this.__wait_for_items = true } | 
					
						
							| 
									
										
										
										
											2020-11-15 03:12:33 +03:00
										 |  |  | 		// can't start while running...
 | 
					
						
							| 
									
										
										
										
											2020-12-16 22:30:04 +03:00
										 |  |  | 		if(this.__state == 'running' || this.__state == 'aborted'){ | 
					
						
							| 
									
										
										
										
											2020-11-15 05:13:35 +03:00
										 |  |  | 			return handle(false) } | 
					
						
							| 
									
										
										
										
											2020-11-15 03:12:33 +03:00
										 |  |  | 		this.__state = 'running' | 
					
						
							| 
									
										
										
										
											2020-12-16 22:30:04 +03:00
										 |  |  | 		// XXX if empty start polling...
 | 
					
						
							| 
									
										
										
										
											2020-12-02 19:53:39 +03:00
										 |  |  | 		this.__run_tasks__(sync) }), | 
					
						
							| 
									
										
										
										
											2020-11-15 03:12:33 +03:00
										 |  |  | 	stop: events.Event('stop', function(handle){ | 
					
						
							|  |  |  | 		// can't stop while not running...
 | 
					
						
							| 
									
										
										
										
											2020-12-16 03:50:59 +03:00
										 |  |  | 		if(this.state == 'stopped' || this.state == 'aborted'){ | 
					
						
							| 
									
										
										
										
											2020-11-15 05:13:35 +03:00
										 |  |  | 			return handle(false) } | 
					
						
							|  |  |  | 		this.__state = 'stopped' }), | 
					
						
							| 
									
										
										
										
											2020-11-07 05:36:20 +03:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-11-07 02:49:32 +03:00
										 |  |  | 	// events...
 | 
					
						
							| 
									
										
										
										
											2020-11-07 05:36:20 +03:00
										 |  |  | 	//
 | 
					
						
							| 
									
										
										
										
											2020-12-01 05:48:50 +03:00
										 |  |  | 	// 	.tasksAdded(func(evt, [task, ..]))
 | 
					
						
							| 
									
										
										
										
											2020-11-30 19:20:33 +03:00
										 |  |  | 	// 	.taskStarting(func(evt, task))
 | 
					
						
							| 
									
										
										
										
											2020-12-21 05:23:16 +03:00
										 |  |  | 	// 	.taskCompleted(func(evt, task, res))
 | 
					
						
							| 
									
										
										
										
											2020-11-30 19:20:33 +03:00
										 |  |  | 	// 	.queueEmpty(func(evt))
 | 
					
						
							|  |  |  | 	//
 | 
					
						
							| 
									
										
										
										
											2020-12-01 05:48:50 +03:00
										 |  |  | 	tasksAdded: events.PureEvent('tasksAdded'), | 
					
						
							| 
									
										
										
										
											2020-11-30 19:20:33 +03:00
										 |  |  | 	taskStarting: events.PureEvent('taskStarting'), | 
					
						
							|  |  |  | 	taskCompleted: events.PureEvent('taskCompleted'), | 
					
						
							| 
									
										
										
										
											2020-12-02 01:16:48 +03:00
										 |  |  | 	taskFailed: events.PureEvent('taskFailed'), | 
					
						
							| 
									
										
										
										
											2020-11-30 19:20:33 +03:00
										 |  |  | 	queueEmpty: events.PureEvent('queueEmpty'), | 
					
						
							| 
									
										
										
										
											2020-11-07 02:49:32 +03:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-11-23 02:59:18 +03:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	// Runner API...
 | 
					
						
							| 
									
										
										
										
											2020-11-22 21:43:35 +03:00
										 |  |  | 	//
 | 
					
						
							| 
									
										
										
										
											2020-11-23 02:59:18 +03:00
										 |  |  | 	//	Run the given task type...
 | 
					
						
							| 
									
										
										
										
											2020-12-02 06:28:11 +03:00
										 |  |  | 	//	.handler(task[, next])
 | 
					
						
							| 
									
										
										
										
											2020-11-23 02:59:18 +03:00
										 |  |  | 	//		-> STOP
 | 
					
						
							|  |  |  | 	//		-> STOP(value)
 | 
					
						
							| 
									
										
										
										
											2020-12-17 18:55:28 +03:00
										 |  |  | 	//		-> SKIP
 | 
					
						
							| 
									
										
										
										
											2020-11-23 06:53:11 +03:00
										 |  |  | 	//		-> queue
 | 
					
						
							| 
									
										
										
										
											2020-11-22 21:43:35 +03:00
										 |  |  | 	//		-> promise
 | 
					
						
							|  |  |  | 	//		-> func
 | 
					
						
							|  |  |  | 	//		-> ...
 | 
					
						
							|  |  |  | 	//
 | 
					
						
							| 
									
										
										
										
											2020-11-23 06:53:11 +03:00
										 |  |  | 	// NOTE: this intentionally does not handle results as that whould 
 | 
					
						
							|  |  |  | 	// 		require this to also handle events, and other runtime stuff...
 | 
					
						
							|  |  |  | 	// 		...to add a new task/result type either handle the non-standard
 | 
					
						
							|  |  |  | 	// 		result here or wrap it into a standard return value like a 
 | 
					
						
							|  |  |  | 	// 		promise...
 | 
					
						
							| 
									
										
										
										
											2020-12-02 06:28:11 +03:00
										 |  |  | 	handler: function(task, next){ | 
					
						
							|  |  |  | 		return typeof(task) == 'function' ? | 
					
						
							| 
									
										
										
										
											2020-11-22 21:43:35 +03:00
										 |  |  | 				task() | 
					
						
							| 
									
										
										
										
											2020-11-23 02:59:18 +03:00
										 |  |  | 			: (task instanceof Queue  | 
					
						
							|  |  |  | 					&& this.sub_queue == 'unwind') ? | 
					
						
							| 
									
										
										
										
											2020-11-23 03:07:41 +03:00
										 |  |  | 				(task.runTask(next), task) | 
					
						
							| 
									
										
										
										
											2020-11-23 02:59:18 +03:00
										 |  |  | 			: (task instanceof Queue  | 
					
						
							|  |  |  | 					&& this.sub_queue == 'wait') ? | 
					
						
							| 
									
										
										
										
											2020-11-22 21:43:35 +03:00
										 |  |  | 				task.start() | 
					
						
							|  |  |  | 			: task }, | 
					
						
							| 
									
										
										
										
											2020-11-23 02:59:18 +03:00
										 |  |  | 	//
 | 
					
						
							| 
									
										
										
										
											2020-11-24 05:48:54 +03:00
										 |  |  | 	// 	Hanlde 'running' state (async)...
 | 
					
						
							| 
									
										
										
										
											2020-11-23 02:59:18 +03:00
										 |  |  | 	// 	.__run_tasks__()
 | 
					
						
							|  |  |  | 	// 		-> this
 | 
					
						
							| 
									
										
										
										
											2020-11-07 05:36:20 +03:00
										 |  |  | 	//
 | 
					
						
							| 
									
										
										
										
											2020-12-02 19:53:39 +03:00
										 |  |  | 	// NOTE: .sync_start affects only the first run...
 | 
					
						
							| 
									
										
										
										
											2020-11-07 05:36:20 +03:00
										 |  |  | 	// NOTE: we do not store the exec results...
 | 
					
						
							| 
									
										
										
										
											2020-11-07 02:49:32 +03:00
										 |  |  | 	// NOTE: not intended for direct use and will likely have no effect
 | 
					
						
							|  |  |  | 	// 		if called directly... 
 | 
					
						
							| 
									
										
										
										
											2020-12-02 21:02:38 +03:00
										 |  |  | 	//
 | 
					
						
							|  |  |  | 	// XXX will this be collected by the GC if it is polling???
 | 
					
						
							| 
									
										
										
										
											2020-11-07 02:49:32 +03:00
										 |  |  | 	__running: null, | 
					
						
							| 
									
										
										
										
											2020-12-02 19:53:39 +03:00
										 |  |  | 	__run_tasks__: function(sync){ | 
					
						
							| 
									
										
										
										
											2020-11-23 02:59:18 +03:00
										 |  |  | 		var that = this | 
					
						
							| 
									
										
										
										
											2020-12-02 19:53:39 +03:00
										 |  |  | 		sync = sync == null ? | 
					
						
							|  |  |  | 				this.sync_start | 
					
						
							|  |  |  | 			: sync == 'async' ? | 
					
						
							|  |  |  | 				false | 
					
						
							|  |  |  | 			: !!sync | 
					
						
							| 
									
										
										
										
											2020-11-24 06:00:51 +03:00
										 |  |  | 
 | 
					
						
							|  |  |  | 		var run = function(){ | 
					
						
							| 
									
										
										
										
											2020-12-02 21:02:38 +03:00
										 |  |  | 			var c = 0 | 
					
						
							|  |  |  | 			var pause = this.pause_after_sync | 
					
						
							|  |  |  | 			var running = this.__running || [] | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 			// run queue...
 | 
					
						
							| 
									
										
										
										
											2020-11-24 06:00:51 +03:00
										 |  |  | 			while(this.length > 0  | 
					
						
							|  |  |  | 					&& this.state == 'running' | 
					
						
							| 
									
										
										
										
											2020-12-02 21:02:38 +03:00
										 |  |  | 					// do not exceed pool size...
 | 
					
						
							|  |  |  | 					&& running.length < this.pool_size | 
					
						
							|  |  |  | 					// do not run too many sync tasks without a break...
 | 
					
						
							|  |  |  | 					&& (pause == null | 
					
						
							|  |  |  | 						|| c < pause)){ | 
					
						
							|  |  |  | 				var p = running.length | 
					
						
							| 
									
										
										
										
											2020-12-16 22:30:04 +03:00
										 |  |  | 				delete this.__wait_for_items | 
					
						
							| 
									
										
										
										
											2020-12-02 21:02:38 +03:00
										 |  |  | 
 | 
					
						
							|  |  |  | 				this.runTask(this.__run_tasks__.bind(this))  | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 				// NOTE: only count sync stuff that does not get added 
 | 
					
						
							|  |  |  | 				// 		to the pool...
 | 
					
						
							|  |  |  | 				p == running.length | 
					
						
							|  |  |  | 					&& c++ } | 
					
						
							| 
									
										
										
										
											2020-11-24 06:00:51 +03:00
										 |  |  | 
 | 
					
						
							|  |  |  | 			// empty queue -> pole or stop...
 | 
					
						
							|  |  |  | 			//
 | 
					
						
							|  |  |  | 			// NOTE: we endup here in two cases:
 | 
					
						
							|  |  |  | 			// 		- the pool is full
 | 
					
						
							|  |  |  | 			// 		- the queue is empty
 | 
					
						
							|  |  |  | 			// NOTE: we do not care about stopping the timer when changing 
 | 
					
						
							|  |  |  | 			// 		state as .__run_tasks__() will stop itself...
 | 
					
						
							| 
									
										
										
										
											2020-12-02 19:53:39 +03:00
										 |  |  | 			if(this.state == 'running'){ | 
					
						
							| 
									
										
										
										
											2020-12-02 21:02:38 +03:00
										 |  |  | 				var timeout =  | 
					
						
							|  |  |  | 					// idle -- empty queue...
 | 
					
						
							|  |  |  | 					this.length == 0 ? | 
					
						
							|  |  |  | 						this.poling_timeout | 
					
						
							|  |  |  | 					// busy poling -- pool full...
 | 
					
						
							|  |  |  | 					: c < pause ? | 
					
						
							| 
									
										
										
										
											2020-12-03 05:17:59 +03:00
										 |  |  | 						//this.busy_timeout
 | 
					
						
							|  |  |  | 						(this.runtime_stats && this.busy_timeout == 'auto' ? | 
					
						
							|  |  |  | 							(this.runtime_stats.avg_t || 50) * (this.busy_timeout_scale || 2) | 
					
						
							|  |  |  | 						: this.busy_timeout == 'auto' ? | 
					
						
							|  |  |  | 							50 * (this.busy_timeout_scale || 2) | 
					
						
							|  |  |  | 						: this.busy_timeout) | 
					
						
							| 
									
										
										
										
											2020-12-02 21:02:38 +03:00
										 |  |  | 					// pause -- let other stuff run...
 | 
					
						
							|  |  |  | 					: (this.pause_timeout || 0) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-12-16 22:30:04 +03:00
										 |  |  | 				;(this.length == 0  | 
					
						
							|  |  |  | 						&& this.auto_stop  | 
					
						
							|  |  |  | 						&& !this.__wait_for_items) ? | 
					
						
							| 
									
										
										
										
											2020-11-24 06:00:51 +03:00
										 |  |  | 					// auto-stop...
 | 
					
						
							| 
									
										
										
										
											2020-12-16 05:17:27 +03:00
										 |  |  | 					this.__onempty__() | 
					
						
							| 
									
										
										
										
											2020-12-02 21:02:38 +03:00
										 |  |  | 					// pole / pause...
 | 
					
						
							|  |  |  | 					: timeout != null | 
					
						
							| 
									
										
										
										
											2020-11-24 06:00:51 +03:00
										 |  |  | 						&& setTimeout( | 
					
						
							| 
									
										
										
										
											2020-12-02 21:02:38 +03:00
										 |  |  | 							this.__run_tasks__.bind(this), timeout) } }.bind(this) | 
					
						
							| 
									
										
										
										
											2020-11-24 06:00:51 +03:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-11-24 05:48:54 +03:00
										 |  |  | 		this.state == 'running' | 
					
						
							| 
									
										
										
										
											2020-12-02 19:53:39 +03:00
										 |  |  | 			&& (sync ? | 
					
						
							| 
									
										
										
										
											2020-11-24 06:00:51 +03:00
										 |  |  | 				run() | 
					
						
							|  |  |  | 				: setTimeout(run, 0)) | 
					
						
							| 
									
										
										
										
											2020-11-23 02:59:18 +03:00
										 |  |  | 		return this }, | 
					
						
							| 
									
										
										
										
											2020-12-16 05:17:27 +03:00
										 |  |  | 	__onempty__: function(){ | 
					
						
							| 
									
										
										
										
											2020-12-16 05:18:58 +03:00
										 |  |  | 		var that = this | 
					
						
							| 
									
										
										
										
											2020-12-16 05:17:27 +03:00
										 |  |  | 		this.poling_timeout != null ? | 
					
						
							|  |  |  | 			// wait a bit then stop if still empty...
 | 
					
						
							|  |  |  | 			setTimeout(function(){ | 
					
						
							|  |  |  | 				that.length > 0 ? | 
					
						
							|  |  |  | 					that.__run_tasks__() | 
					
						
							| 
									
										
										
										
											2020-12-16 05:18:58 +03:00
										 |  |  | 					: that.stop() | 
					
						
							| 
									
										
										
										
											2020-12-16 05:17:27 +03:00
										 |  |  | 				}, this.poling_timeout) | 
					
						
							|  |  |  | 			// stop now...
 | 
					
						
							|  |  |  | 			: this.stop() | 
					
						
							|  |  |  | 		return this }, | 
					
						
							| 
									
										
										
										
											2020-11-23 02:59:18 +03:00
										 |  |  | 	// run one task from queue...
 | 
					
						
							|  |  |  | 	// NOTE: this does not care about .state...
 | 
					
						
							| 
									
										
										
										
											2020-12-02 05:29:38 +03:00
										 |  |  | 	// XXX revise error handling...
 | 
					
						
							| 
									
										
										
										
											2020-12-16 03:50:59 +03:00
										 |  |  | 	// XXX ABORT: added nested abort support...
 | 
					
						
							|  |  |  | 	__results: null, | 
					
						
							| 
									
										
										
										
											2020-11-23 03:07:41 +03:00
										 |  |  | 	runTask: function(next){ | 
					
						
							| 
									
										
										
										
											2020-11-30 19:20:33 +03:00
										 |  |  | 		var that = this | 
					
						
							| 
									
										
										
										
											2020-11-23 02:59:18 +03:00
										 |  |  | 		var running = this.__running = this.__running || [] | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		// can't run... 
 | 
					
						
							|  |  |  | 		if(this.length == 0 | 
					
						
							|  |  |  | 				|| running.length >= this.pool_size ){ | 
					
						
							|  |  |  | 			return this } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-11-23 06:53:11 +03:00
										 |  |  | 		// closure: running, task, res, stop, next...
 | 
					
						
							| 
									
										
										
										
											2020-12-03 05:17:59 +03:00
										 |  |  | 		var taskCompleted = function(){ | 
					
						
							|  |  |  | 			// calculate runtime statistics...
 | 
					
						
							|  |  |  | 			if(that.runtime_stats){ | 
					
						
							|  |  |  | 				var x = Date.now() - t0 | 
					
						
							|  |  |  | 				var s = that.runtime_stats =  | 
					
						
							|  |  |  | 					that.runtime_stats  | 
					
						
							|  |  |  | 						|| {max_t: x, min_t: x, avg_t: x, count: 0} | 
					
						
							|  |  |  | 				s.max_t = Math.max(s.max_t, x) | 
					
						
							|  |  |  | 				s.min_t = Math.min(s.min_t, x) | 
					
						
							|  |  |  | 				var i = ++s.count | 
					
						
							|  |  |  | 				var a = s.avg_t  | 
					
						
							|  |  |  | 				s.avg_t = a + (x - a)/i } | 
					
						
							| 
									
										
										
										
											2020-12-21 05:23:16 +03:00
										 |  |  | 			// report...
 | 
					
						
							| 
									
										
										
										
											2020-12-03 05:17:59 +03:00
										 |  |  | 			that.trigger('taskCompleted', task, res) } | 
					
						
							| 
									
										
										
										
											2020-12-21 05:23:16 +03:00
										 |  |  | 		var fail = {doc: 'fail runningDone(..)'} | 
					
						
							|  |  |  | 		var runningDone = function(mode){ | 
					
						
							| 
									
										
										
										
											2020-11-23 02:59:18 +03:00
										 |  |  | 			running.splice(0, running.length,  | 
					
						
							| 
									
										
										
										
											2020-12-21 05:23:16 +03:00
										 |  |  | 				// NOTE: there can be multiple occurrences of res...
 | 
					
						
							| 
									
										
										
										
											2020-11-23 02:59:18 +03:00
										 |  |  | 				...running | 
					
						
							| 
									
										
										
										
											2020-11-23 06:53:11 +03:00
										 |  |  | 					.filter(function(e){ return e !== res }))  | 
					
						
							| 
									
										
										
										
											2020-12-21 05:23:16 +03:00
										 |  |  | 			mode === fail | 
					
						
							|  |  |  | 				|| taskCompleted() | 
					
						
							| 
									
										
										
										
											2020-11-23 06:53:11 +03:00
										 |  |  | 			!stop && next | 
					
						
							|  |  |  | 				&& next() } | 
					
						
							| 
									
										
										
										
											2020-11-23 02:59:18 +03:00
										 |  |  | 
 | 
					
						
							|  |  |  | 		var task = this.shift() | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		this.trigger('taskStarting', task) | 
					
						
							| 
									
										
										
										
											2020-12-03 05:17:59 +03:00
										 |  |  | 		var t0 = this.runtime_stats && Date.now() | 
					
						
							| 
									
										
										
										
											2020-11-23 02:59:18 +03:00
										 |  |  | 
 | 
					
						
							|  |  |  | 		// run...
 | 
					
						
							| 
									
										
										
										
											2020-12-02 01:16:48 +03:00
										 |  |  | 		// catch and pass errors to .taskFailed(...)
 | 
					
						
							|  |  |  | 		if(this.catch_errors){ | 
					
						
							|  |  |  | 			var err | 
					
						
							|  |  |  | 			try { | 
					
						
							| 
									
										
										
										
											2020-12-02 06:28:11 +03:00
										 |  |  | 				var res = this.handler(task, next) | 
					
						
							| 
									
										
										
										
											2020-12-02 01:16:48 +03:00
										 |  |  | 
 | 
					
						
							|  |  |  | 			} catch(err){ | 
					
						
							|  |  |  | 				this.trigger('taskFailed', task, err) } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 			// promise result...
 | 
					
						
							|  |  |  | 			// XXX is the err test here needed???
 | 
					
						
							| 
									
										
										
										
											2020-12-02 01:19:42 +03:00
										 |  |  | 			res  | 
					
						
							|  |  |  | 				&& err === undefined  | 
					
						
							| 
									
										
										
										
											2020-12-02 01:18:48 +03:00
										 |  |  | 				&& res.catch | 
					
						
							|  |  |  | 				&& res.catch(function(err){ | 
					
						
							| 
									
										
										
										
											2020-12-02 01:16:48 +03:00
										 |  |  | 					that.trigger('taskFailed', task, err) }) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-12-21 05:23:16 +03:00
										 |  |  | 		// let errors rain...
 | 
					
						
							| 
									
										
										
										
											2020-12-02 01:16:48 +03:00
										 |  |  | 		} else { | 
					
						
							| 
									
										
										
										
											2020-12-02 06:28:11 +03:00
										 |  |  | 			var res = this.handler(task, next) } | 
					
						
							| 
									
										
										
										
											2020-11-23 02:59:18 +03:00
										 |  |  | 
 | 
					
						
							|  |  |  | 		// handle stop...
 | 
					
						
							|  |  |  | 		var stop = res === module.STOP  | 
					
						
							|  |  |  | 			|| res instanceof module.STOP | 
					
						
							|  |  |  | 		res = res instanceof module.STOP ? | 
					
						
							| 
									
										
										
										
											2020-12-15 04:02:38 +03:00
										 |  |  | 				res.value | 
					
						
							|  |  |  | 			: res === module.STOP ? | 
					
						
							|  |  |  | 				undefined | 
					
						
							| 
									
										
										
										
											2020-11-23 02:59:18 +03:00
										 |  |  | 			: res | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-12-15 04:02:38 +03:00
										 |  |  | 		// collect results...
 | 
					
						
							|  |  |  | 		this.collect_results | 
					
						
							| 
									
										
										
										
											2020-12-17 18:55:28 +03:00
										 |  |  | 			&& res !== module.SKIP | 
					
						
							| 
									
										
										
										
											2020-12-15 04:02:38 +03:00
										 |  |  | 			&& (this.__results = this.__results || []).push(res) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-11-23 02:59:18 +03:00
										 |  |  | 		// handle task results...
 | 
					
						
							|  |  |  | 		//
 | 
					
						
							|  |  |  | 		// queue -- as a set of tasks...
 | 
					
						
							|  |  |  | 		if(res instanceof Queue | 
					
						
							|  |  |  | 				&& this.sub_queue == 'unwind'){ | 
					
						
							|  |  |  | 			if(res.length > 0){ | 
					
						
							|  |  |  | 				this.push(res) } | 
					
						
							| 
									
										
										
										
											2020-12-03 05:17:59 +03:00
										 |  |  | 			taskCompleted() | 
					
						
							| 
									
										
										
										
											2020-11-23 02:59:18 +03:00
										 |  |  | 
 | 
					
						
							|  |  |  | 		// queue -- as a task...
 | 
					
						
							|  |  |  | 		} else if(res instanceof Queue | 
					
						
							|  |  |  | 				&& this.sub_queue == 'wait'){ | 
					
						
							|  |  |  | 			if(res.state == 'stopped'){ | 
					
						
							| 
									
										
										
										
											2020-12-03 05:17:59 +03:00
										 |  |  | 				taskCompleted() | 
					
						
							| 
									
										
										
										
											2020-11-23 02:59:18 +03:00
										 |  |  | 
 | 
					
						
							|  |  |  | 			} else { | 
					
						
							|  |  |  | 				running.push(res) | 
					
						
							|  |  |  | 				res.stop(function(){ | 
					
						
							| 
									
										
										
										
											2020-11-23 06:53:11 +03:00
										 |  |  | 					// not fully done yet -- re-queue... 
 | 
					
						
							| 
									
										
										
										
											2020-11-23 02:59:18 +03:00
										 |  |  | 					res.length > 0 | 
					
						
							|  |  |  | 						&& that.push(res)  | 
					
						
							| 
									
										
										
										
											2020-11-23 06:53:11 +03:00
										 |  |  | 					runningDone() }) } | 
					
						
							| 
									
										
										
										
											2020-11-23 02:59:18 +03:00
										 |  |  | 
 | 
					
						
							|  |  |  | 		// pool async (promise) task...
 | 
					
						
							| 
									
										
										
										
											2020-12-21 05:23:16 +03:00
										 |  |  | 		// XXX REVISE...
 | 
					
						
							|  |  |  | 		// XXX do we need to report errors???
 | 
					
						
							|  |  |  | 		} else if(typeof((res || {}).then) == 'function' | 
					
						
							|  |  |  | 				// one post handler is enough... 
 | 
					
						
							|  |  |  | 				// XXX will this prevent some tasks from reporting???
 | 
					
						
							| 
									
										
										
										
											2020-11-23 02:59:18 +03:00
										 |  |  | 				&& !running.includes(res)){ | 
					
						
							|  |  |  | 			running.push(res)  | 
					
						
							| 
									
										
										
										
											2020-12-21 05:23:16 +03:00
										 |  |  | 			//res.finally(runningDone)
 | 
					
						
							|  |  |  | 			res.then( | 
					
						
							|  |  |  | 				runningDone, | 
					
						
							|  |  |  | 				...(this.catch_errors ? | 
					
						
							|  |  |  | 					[function(err){ | 
					
						
							|  |  |  | 						runningDone(fail) | 
					
						
							|  |  |  | 						that.trigger('taskFailed', task, err) }] | 
					
						
							|  |  |  | 					// let errors propagate...
 | 
					
						
							|  |  |  | 					: [])) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		// func -> re-queue tasks...
 | 
					
						
							| 
									
										
										
										
											2020-11-23 02:59:18 +03:00
										 |  |  | 		} else if(typeof(res) == 'function'){ | 
					
						
							| 
									
										
										
										
											2020-12-03 05:17:59 +03:00
										 |  |  | 			taskCompleted() | 
					
						
							| 
									
										
										
										
											2020-11-23 02:59:18 +03:00
										 |  |  | 			this.push(res) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		// completed sync task...
 | 
					
						
							|  |  |  | 		} else { | 
					
						
							| 
									
										
										
										
											2020-12-03 05:17:59 +03:00
										 |  |  | 			taskCompleted() } | 
					
						
							| 
									
										
										
										
											2020-11-07 02:49:32 +03:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-11-23 03:15:23 +03:00
										 |  |  | 		this.length == 0 | 
					
						
							|  |  |  | 			&& this.trigger('queueEmpty') | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-12-01 05:48:50 +03:00
										 |  |  | 		stop | 
					
						
							|  |  |  | 			&& this.stop() | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-12-15 04:02:38 +03:00
										 |  |  | 		return res }, | 
					
						
							| 
									
										
										
										
											2020-12-01 05:48:50 +03:00
										 |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// helpers...
 | 
					
						
							|  |  |  | 	//
 | 
					
						
							|  |  |  | 	// move tasks to head/tail of queue resp.
 | 
					
						
							|  |  |  | 	prioritize: function(...tasks){ | 
					
						
							|  |  |  | 		return this.sortAs(tasks) }, | 
					
						
							|  |  |  | 	delay: function(...tasks){ | 
					
						
							|  |  |  | 		return this.sortAs(tasks, true) }, | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-12-02 01:16:48 +03:00
										 |  |  | 	// edit/add API...
 | 
					
						
							|  |  |  | 	//
 | 
					
						
							|  |  |  | 	// trigger .tasksAdded(..) on relevant methods...
 | 
					
						
							| 
									
										
										
										
											2020-12-01 05:48:50 +03:00
										 |  |  | 	//
 | 
					
						
							|  |  |  | 	// NOTE: adding tasks via the [..] notation will not trigger the 
 | 
					
						
							|  |  |  | 	// 		event...
 | 
					
						
							| 
									
										
										
										
											2021-01-12 05:14:14 +03:00
										 |  |  | 	// NOTE: the events will not be triggered on no-op calls...
 | 
					
						
							| 
									
										
										
										
											2020-12-22 06:09:47 +03:00
										 |  |  | 	//
 | 
					
						
							| 
									
										
										
										
											2020-12-04 06:30:31 +03:00
										 |  |  | 	// XXX add methods that can shorten the queue (like .pop()/.shift()/..)
 | 
					
						
							|  |  |  | 	// 		to test and trigger .queueEmpty()
 | 
					
						
							|  |  |  | 	// 		...this is not and will not be done on polling as that would 
 | 
					
						
							|  |  |  | 	// 		introduce issues -- queue can change between task runs... (revise!)
 | 
					
						
							| 
									
										
										
										
											2020-12-01 05:48:50 +03:00
										 |  |  | 	push: function(...tasks){ | 
					
						
							| 
									
										
										
										
											2020-12-14 19:50:39 +03:00
										 |  |  | 		var res = object.parentCall(Queue.prototype.push, this, ...tasks) | 
					
						
							| 
									
										
										
										
											2021-01-12 05:14:14 +03:00
										 |  |  | 		tasks.length > 0 | 
					
						
							|  |  |  | 			&& this.trigger('tasksAdded', tasks) | 
					
						
							| 
									
										
										
										
											2020-12-01 05:48:50 +03:00
										 |  |  | 		return res }, | 
					
						
							|  |  |  | 	unsift: function(...tasks){ | 
					
						
							| 
									
										
										
										
											2020-12-14 19:50:39 +03:00
										 |  |  | 		var res = object.parentCall(Queue.prototype.unshift, this, ...tasks) | 
					
						
							| 
									
										
										
										
											2021-01-12 05:14:14 +03:00
										 |  |  | 		tasks.length > 0 | 
					
						
							|  |  |  | 			&& this.trigger('tasksAdded', tasks) | 
					
						
							| 
									
										
										
										
											2020-12-01 05:48:50 +03:00
										 |  |  | 		return res }, | 
					
						
							|  |  |  | 	splice: function(...args){ | 
					
						
							| 
									
										
										
										
											2020-12-04 06:30:31 +03:00
										 |  |  | 		var l = this.length | 
					
						
							| 
									
										
										
										
											2020-12-14 19:50:39 +03:00
										 |  |  | 		var res = object.parentCall(Queue.prototype.splice, this, ...args) | 
					
						
							| 
									
										
										
										
											2020-12-01 22:18:02 +03:00
										 |  |  | 		var tasks = args.slice(2) | 
					
						
							| 
									
										
										
										
											2020-12-01 05:48:50 +03:00
										 |  |  | 		tasks.length > 0 | 
					
						
							|  |  |  | 			&& this.trigger('tasksAdded', tasks) | 
					
						
							| 
									
										
										
										
											2021-01-12 05:14:14 +03:00
										 |  |  | 		// length changed...
 | 
					
						
							| 
									
										
										
										
											2020-12-04 06:30:31 +03:00
										 |  |  | 		l != 0 && this.length == 0 | 
					
						
							|  |  |  | 			&& this.trigger('queueEmpty') | 
					
						
							| 
									
										
										
										
											2020-12-01 05:48:50 +03:00
										 |  |  | 		return res }, | 
					
						
							| 
									
										
										
										
											2020-12-03 05:17:59 +03:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-12-02 01:16:48 +03:00
										 |  |  | 	// shorthands...
 | 
					
						
							| 
									
										
										
										
											2020-12-22 06:09:47 +03:00
										 |  |  | 	//
 | 
					
						
							|  |  |  | 	// NOTE: this helps get around the argument number limitation in JS...
 | 
					
						
							|  |  |  | 	add: function(tasks){ | 
					
						
							|  |  |  | 		// handle too large a number of args...
 | 
					
						
							|  |  |  | 		var MAX_ARGS = 10000 | 
					
						
							|  |  |  | 		if(tasks.length > MAX_ARGS){ | 
					
						
							|  |  |  | 			while(tasks.length > 0){ | 
					
						
							|  |  |  | 				this.push(...tasks.splice(0, MAX_ARGS)) } | 
					
						
							|  |  |  | 			return this } | 
					
						
							| 
									
										
										
										
											2020-12-01 05:48:50 +03:00
										 |  |  | 		this.push(...tasks) | 
					
						
							| 
									
										
										
										
											2020-11-07 02:49:32 +03:00
										 |  |  | 		return this }, | 
					
						
							| 
									
										
										
										
											2020-12-16 03:50:59 +03:00
										 |  |  | 	// NOTE: this will also clear the results cache...
 | 
					
						
							|  |  |  | 	clear: function(full=false){ | 
					
						
							|  |  |  | 		full | 
					
						
							|  |  |  | 			&& (delete this.__results) | 
					
						
							| 
									
										
										
										
											2020-12-04 06:30:31 +03:00
										 |  |  | 		this.splice(0, this.length) }, | 
					
						
							| 
									
										
										
										
											2020-12-02 01:16:48 +03:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-11-07 02:49:32 +03:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-11-07 05:36:20 +03:00
										 |  |  | 	// constructor argument handling...
 | 
					
						
							|  |  |  | 	//
 | 
					
						
							|  |  |  | 	// 	Queue()
 | 
					
						
							|  |  |  | 	// 		-> queue
 | 
					
						
							|  |  |  | 	//
 | 
					
						
							|  |  |  | 	// 	Queue(..,tasks)
 | 
					
						
							|  |  |  | 	// 		-> queue
 | 
					
						
							|  |  |  | 	//
 | 
					
						
							|  |  |  | 	// 	Queue(options)
 | 
					
						
							|  |  |  | 	// 	Queue(options, ..,tasks)
 | 
					
						
							|  |  |  | 	// 		-> queue
 | 
					
						
							|  |  |  | 	//
 | 
					
						
							| 
									
										
										
										
											2020-11-07 02:49:32 +03:00
										 |  |  | 	__init__: function(options){  | 
					
						
							| 
									
										
										
										
											2020-11-23 02:59:18 +03:00
										 |  |  | 		// options...
 | 
					
						
							|  |  |  | 		if(!(this[0] instanceof Queue) | 
					
						
							|  |  |  | 				&& this[0] instanceof Object  | 
					
						
							| 
									
										
										
										
											2020-11-07 02:49:32 +03:00
										 |  |  | 				&& typeof(this[0]) != 'function' | 
					
						
							| 
									
										
										
										
											2020-12-08 01:53:40 +03:00
										 |  |  | 				// XXX do we need this test???
 | 
					
						
							| 
									
										
										
										
											2020-11-07 02:49:32 +03:00
										 |  |  | 				&& typeof(this[0].finally) != 'function'){ | 
					
						
							|  |  |  | 			Object.assign(this, this.shift()) } | 
					
						
							| 
									
										
										
										
											2020-12-01 05:48:50 +03:00
										 |  |  | 		this.length > 0 | 
					
						
							|  |  |  | 			&& this.trigger('tasksAdded', [...this]) | 
					
						
							| 
									
										
										
										
											2020-11-23 02:59:18 +03:00
										 |  |  | 		// see if we need to start...
 | 
					
						
							|  |  |  | 		this.__run_tasks__() }, | 
					
						
							| 
									
										
										
										
											2020-11-19 02:24:09 +03:00
										 |  |  | })) | 
					
						
							| 
									
										
										
										
											2020-11-07 02:49:32 +03:00
										 |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-12-17 18:55:28 +03:00
										 |  |  | // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 
 | 
					
						
							| 
									
										
										
										
											2020-12-16 05:17:27 +03:00
										 |  |  | // Like Queue(..) but adds terminal states and conversion to promises...
 | 
					
						
							|  |  |  | //
 | 
					
						
							| 
									
										
										
										
											2020-12-17 18:55:28 +03:00
										 |  |  | // XXX should this .freeze()??/
 | 
					
						
							| 
									
										
										
										
											2020-12-16 05:17:27 +03:00
										 |  |  | // XXX find a better name...
 | 
					
						
							|  |  |  | var FinalizableQueue = | 
					
						
							|  |  |  | module.FinalizableQueue = | 
					
						
							|  |  |  | object.Constructor('FinalizableQueue', Queue, { | 
					
						
							| 
									
										
										
										
											2020-12-16 22:30:04 +03:00
										 |  |  | 	auto_stop: true, | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-12-16 05:17:27 +03:00
										 |  |  | 	__onempty__: function(){ | 
					
						
							|  |  |  | 		return this.trigger('done') }, | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	done: events.Event('done', function(handle){ | 
					
						
							|  |  |  | 		// abort only once...
 | 
					
						
							|  |  |  | 		if(this.state == 'aborted' || this.state == 'done'){ | 
					
						
							|  |  |  | 			return handle(false) } | 
					
						
							|  |  |  | 		this.__state = 'done'  | 
					
						
							|  |  |  | 		Object.freeze(this) }), | 
					
						
							|  |  |  | 	abort: events.Event('abort', function(handle){ | 
					
						
							|  |  |  | 		// abort only once...
 | 
					
						
							|  |  |  | 		if(this.state == 'aborted' || this.state == 'done'){ | 
					
						
							|  |  |  | 			return handle(false) } | 
					
						
							|  |  |  | 		this.__state = 'aborted'  | 
					
						
							|  |  |  | 		Object.freeze(this) }), | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// NOTE: each handler will get called once when the next time the 
 | 
					
						
							|  |  |  | 	// 		queue is emptied...
 | 
					
						
							|  |  |  | 	promise: function(){ | 
					
						
							|  |  |  | 		var that = this | 
					
						
							|  |  |  | 		return new Promise(function(resolve, reject){ | 
					
						
							|  |  |  | 			that | 
					
						
							|  |  |  | 				.one('done', function(){ | 
					
						
							|  |  |  | 					resolve(...(that.collect_results ?  | 
					
						
							|  |  |  | 						[that.__results || []] | 
					
						
							|  |  |  | 						: [])) }) | 
					
						
							|  |  |  | 				.one('abort', reject) }) }, | 
					
						
							|  |  |  | 	then: function(onresolve, onreject){ | 
					
						
							|  |  |  | 		return this.promise().then(...arguments) }, | 
					
						
							|  |  |  | 	catch: function(onreject){ | 
					
						
							|  |  |  | 		return this.promise().catch(...arguments) }, | 
					
						
							|  |  |  | }) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-11-27 18:18:07 +03:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-11-24 05:48:54 +03:00
										 |  |  | //---------------------------------------------------------------------
 | 
					
						
							|  |  |  | // Task manager...
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-11-27 18:18:07 +03:00
										 |  |  | // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 
 | 
					
						
							|  |  |  | // Helpres...
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // Task ticket...
 | 
					
						
							|  |  |  | //
 | 
					
						
							|  |  |  | // This lets the client control the task object and receive messages 
 | 
					
						
							|  |  |  | // from it.
 | 
					
						
							| 
									
										
										
										
											2020-11-26 22:56:23 +03:00
										 |  |  | //
 | 
					
						
							|  |  |  | // NOTE: this is not intended for direct use...
 | 
					
						
							| 
									
										
										
										
											2020-11-25 20:39:55 +03:00
										 |  |  | var TaskTicket = | 
					
						
							| 
									
										
										
										
											2020-11-27 18:18:07 +03:00
										 |  |  | // XXX do we let the user see this???
 | 
					
						
							|  |  |  | module.TaskTicket = | 
					
						
							| 
									
										
										
										
											2020-11-26 22:56:23 +03:00
										 |  |  | object.Constructor('TaskTicket', Promise, { | 
					
						
							| 
									
										
										
										
											2020-11-25 20:39:55 +03:00
										 |  |  | 	__data: null, | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	title: null, | 
					
						
							| 
									
										
										
										
											2020-11-26 23:28:29 +03:00
										 |  |  | 	task: null, | 
					
						
							| 
									
										
										
										
											2020-11-25 20:39:55 +03:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-11-26 22:56:23 +03:00
										 |  |  | 	get state(){ | 
					
						
							|  |  |  | 		return this.__data.state }, | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-11-25 20:39:55 +03:00
										 |  |  | 	resolve: function(...args){ | 
					
						
							| 
									
										
										
										
											2020-11-26 22:56:23 +03:00
										 |  |  | 		if(this.__data.state == 'pending'){ | 
					
						
							|  |  |  | 			this.__data.state = 'resolved' | 
					
						
							|  |  |  | 			this.__data.resolve(...args) } | 
					
						
							| 
									
										
										
										
											2020-11-25 20:39:55 +03:00
										 |  |  | 		return this }, | 
					
						
							|  |  |  | 	reject: function(...args){ | 
					
						
							| 
									
										
										
										
											2020-11-26 22:56:23 +03:00
										 |  |  | 		if(this.__data.state == 'pending'){ | 
					
						
							|  |  |  | 			this.__data.state = 'rejected' | 
					
						
							|  |  |  | 			this.__data.reject(...args) } | 
					
						
							| 
									
										
										
										
											2020-11-25 20:39:55 +03:00
										 |  |  | 		return this }, | 
					
						
							|  |  |  | 	onmessage: function(msg, func){ | 
					
						
							|  |  |  | 		this.__data.onmessage( | 
					
						
							|  |  |  | 			typeof(msg) == 'function' ? | 
					
						
							|  |  |  | 				msg | 
					
						
							|  |  |  | 				: function(m, ...args){ | 
					
						
							|  |  |  | 					m == msg | 
					
						
							|  |  |  | 						&& func(...args) }) | 
					
						
							|  |  |  | 		return this }, | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-11-26 22:56:23 +03:00
										 |  |  | 	then: Promise.iter.prototype.then, | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-11-26 23:28:29 +03:00
										 |  |  | 	__new__: function(_, title, resolve, reject, onmessage, task){ | 
					
						
							| 
									
										
										
										
											2020-11-26 22:56:23 +03:00
										 |  |  | 		var handlers | 
					
						
							|  |  |  | 		var resolver = arguments[1] | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		var obj = Reflect.construct( | 
					
						
							|  |  |  | 			TaskTicket.__proto__,  | 
					
						
							|  |  |  | 			[function(resolve, reject){ | 
					
						
							|  |  |  | 				handlers = {resolve, reject}  | 
					
						
							|  |  |  | 				// NOTE: this is here to support builtin .then(..)
 | 
					
						
							|  |  |  | 				typeof(resolver) == 'function' | 
					
						
							|  |  |  | 					&& resolver(resolve, reject) }],  | 
					
						
							|  |  |  | 			TaskTicket)  | 
					
						
							| 
									
										
										
										
											2020-11-27 18:18:07 +03:00
										 |  |  | 		// if we got a resolver then it's an internal constructor we are
 | 
					
						
							|  |  |  | 		// not using (likely in base .then(..)) so there is no point in 
 | 
					
						
							|  |  |  | 		// moving on...
 | 
					
						
							|  |  |  | 		// NOTE: this may be a potential source of bugs so we need to 
 | 
					
						
							|  |  |  | 		// 		keep tracking this (XXX)
 | 
					
						
							| 
									
										
										
										
											2020-11-26 22:56:23 +03:00
										 |  |  | 		if(typeof(resolver) == 'function'){ | 
					
						
							|  |  |  | 			return obj } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		// bind this to external resolve/reject...
 | 
					
						
							|  |  |  | 		obj.then( | 
					
						
							|  |  |  | 			function(){ | 
					
						
							|  |  |  | 				resolve(...arguments) },  | 
					
						
							|  |  |  | 			function(){ | 
					
						
							|  |  |  | 				reject(...arguments) }) | 
					
						
							|  |  |  | 		// setup state...
 | 
					
						
							|  |  |  | 		obj.title = title | 
					
						
							| 
									
										
										
										
											2020-11-26 23:28:29 +03:00
										 |  |  | 		obj.task = task | 
					
						
							| 
									
										
										
										
											2020-11-26 22:56:23 +03:00
										 |  |  | 		Object.defineProperty(obj, '__data', { | 
					
						
							|  |  |  | 			value: { | 
					
						
							|  |  |  | 				resolve: handlers.resolve,  | 
					
						
							|  |  |  | 				reject: handlers.reject,  | 
					
						
							|  |  |  | 				onmessage, | 
					
						
							|  |  |  | 				state: 'pending', | 
					
						
							|  |  |  | 			}, | 
					
						
							| 
									
										
										
										
											2020-11-25 20:39:55 +03:00
										 |  |  | 			enumerable: false, | 
					
						
							| 
									
										
										
										
											2020-11-26 22:56:23 +03:00
										 |  |  | 		})  | 
					
						
							|  |  |  | 		return obj }, | 
					
						
							| 
									
										
										
										
											2020-11-25 20:39:55 +03:00
										 |  |  | }) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-11-25 23:57:46 +03:00
										 |  |  | // NOTE: this is not intended for direct use...
 | 
					
						
							|  |  |  | var TaskMixin =  | 
					
						
							|  |  |  | module.TaskMixin = | 
					
						
							|  |  |  | object.Mixin('TaskMixin', 'soft', { | 
					
						
							|  |  |  | 	// standard messages...
 | 
					
						
							|  |  |  | 	stop: function(){ | 
					
						
							|  |  |  | 		this.send('stop', ...arguments) }, | 
					
						
							|  |  |  | }) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-11-27 18:18:07 +03:00
										 |  |  | 
 | 
					
						
							|  |  |  | // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 
 | 
					
						
							|  |  |  | // Task manager...
 | 
					
						
							|  |  |  | //
 | 
					
						
							|  |  |  | // Externally manage/influence long running tasks...
 | 
					
						
							|  |  |  | //
 | 
					
						
							|  |  |  | // A task can be:
 | 
					
						
							|  |  |  | // 	- Promise.interactive(..)
 | 
					
						
							|  |  |  | // 	- Queue(..)
 | 
					
						
							|  |  |  | // 	- function(ticket, ..)
 | 
					
						
							|  |  |  | // 	- object supporting task protocol
 | 
					
						
							|  |  |  | //
 | 
					
						
							|  |  |  | //
 | 
					
						
							|  |  |  | // The task is controlled by passing messages, default messages include:
 | 
					
						
							|  |  |  | // 	- .stop(..)
 | 
					
						
							|  |  |  | //
 | 
					
						
							|  |  |  | //
 | 
					
						
							|  |  |  | // Task protocol:
 | 
					
						
							|  |  |  | // 	.then(..)		- registers a completion handler (a-la Promise)
 | 
					
						
							|  |  |  | // 	.stop(..)		- triggers a task to stop
 | 
					
						
							|  |  |  | //
 | 
					
						
							|  |  |  | //
 | 
					
						
							|  |  |  | // NOTE: we should keep the API here similar to Queue...
 | 
					
						
							|  |  |  | // 		...but this is not a queue in principle (internal vs. external 
 | 
					
						
							| 
									
										
										
										
											2020-11-25 14:54:27 +03:00
										 |  |  | // 		management) so we'll also need to keep them different enough to 
 | 
					
						
							|  |  |  | // 		avoid confusion...
 | 
					
						
							| 
									
										
										
										
											2020-11-30 19:20:33 +03:00
										 |  |  | //
 | 
					
						
							|  |  |  | // XXX should a task manager have a pool size???
 | 
					
						
							|  |  |  | // 		...if yes it would be fun to use the queue to manage the pool...
 | 
					
						
							| 
									
										
										
										
											2020-12-16 03:50:59 +03:00
										 |  |  | // XXX revise .abort(..)
 | 
					
						
							| 
									
										
										
										
											2020-11-24 05:48:54 +03:00
										 |  |  | var TaskManager = | 
					
						
							|  |  |  | module.TaskManager = | 
					
						
							|  |  |  | object.Constructor('TaskManager', Array, events.EventMixin('flat', { | 
					
						
							| 
									
										
										
										
											2020-11-25 20:54:55 +03:00
										 |  |  | 	__task_ticket__: TaskTicket, | 
					
						
							| 
									
										
										
										
											2020-11-25 23:57:46 +03:00
										 |  |  | 	__task_mixin__: TaskMixin, | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-11-28 00:22:27 +03:00
										 |  |  | 	// settings...
 | 
					
						
							|  |  |  | 	//
 | 
					
						
							|  |  |  | 	// if true start/end times will be set on the task:
 | 
					
						
							|  |  |  | 	// 	.time_started
 | 
					
						
							|  |  |  | 	// 	.time_ended
 | 
					
						
							|  |  |  | 	record_times: true, | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// if true the task will be started sync before .Task(..) is returns..
 | 
					
						
							|  |  |  | 	//
 | 
					
						
							|  |  |  | 	// NOTE: this is not recommended as the default as this can block the
 | 
					
						
							|  |  |  | 	// 		manager...
 | 
					
						
							| 
									
										
										
										
											2020-11-25 23:57:46 +03:00
										 |  |  | 	sync_start: false, | 
					
						
							| 
									
										
										
										
											2020-11-25 20:54:55 +03:00
										 |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-11-25 04:04:44 +03:00
										 |  |  | 	//
 | 
					
						
							| 
									
										
										
										
											2020-11-25 16:34:15 +03:00
										 |  |  | 	//	.titled(title)
 | 
					
						
							|  |  |  | 	//	.titled(title, ..)
 | 
					
						
							| 
									
										
										
										
											2020-11-25 04:04:44 +03:00
										 |  |  | 	//		-> manager
 | 
					
						
							|  |  |  | 	//
 | 
					
						
							| 
									
										
										
										
											2020-11-25 16:34:15 +03:00
										 |  |  | 	titled: function(title){ | 
					
						
							| 
									
										
										
										
											2020-11-25 16:36:42 +03:00
										 |  |  | 		if(title == 'all' || title == '*'){ | 
					
						
							|  |  |  | 			return this } | 
					
						
							|  |  |  | 		var titles = new Set([...arguments]) | 
					
						
							| 
									
										
										
										
											2020-11-25 04:04:44 +03:00
										 |  |  | 		return this | 
					
						
							|  |  |  | 			.filter(function(task){  | 
					
						
							| 
									
										
										
										
											2020-12-04 06:13:38 +03:00
										 |  |  | 				return titles.has(task.title)  | 
					
						
							|  |  |  | 					|| titles.has(task.name) }) }, | 
					
						
							| 
									
										
										
										
											2020-11-25 16:34:15 +03:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-11-25 20:39:55 +03:00
										 |  |  | 	// actions...
 | 
					
						
							|  |  |  | 	//
 | 
					
						
							| 
									
										
										
										
											2020-12-03 17:53:25 +03:00
										 |  |  | 	// commands to test as methods...
 | 
					
						
							|  |  |  | 	// 	i.e. task.send(cmd, ...args) -> task[cmd](...args)
 | 
					
						
							| 
									
										
										
										
											2020-12-16 03:50:59 +03:00
										 |  |  | 	__send_commands__: [ 'stop', 'abort'], | 
					
						
							| 
									
										
										
										
											2020-11-25 16:34:15 +03:00
										 |  |  | 	send: function(title, ...args){ | 
					
						
							| 
									
										
										
										
											2020-12-03 17:53:25 +03:00
										 |  |  | 		var that = this | 
					
						
							| 
									
										
										
										
											2020-11-25 16:34:15 +03:00
										 |  |  | 		if(title == 'all' || title == '*'){ | 
					
						
							| 
									
										
										
										
											2020-12-16 03:50:59 +03:00
										 |  |  | 			;[...this].forEach(function(task){ | 
					
						
							| 
									
										
										
										
											2020-12-03 17:53:25 +03:00
										 |  |  | 				'send' in task ? | 
					
						
							|  |  |  | 					task.send(...args)  | 
					
						
							|  |  |  | 				: that.__send_commands__.includes(args[0]) ? | 
					
						
							|  |  |  | 					task[args[0]](...args.slice(1)) | 
					
						
							|  |  |  | 				// XXX
 | 
					
						
							|  |  |  | 				: console.warn('.send(..): can\'t .send(..) to:', task)  | 
					
						
							|  |  |  | 			}) | 
					
						
							| 
									
										
										
										
											2020-11-25 16:34:15 +03:00
										 |  |  | 			return this } | 
					
						
							|  |  |  | 		return this.titled( | 
					
						
							|  |  |  | 				...(title instanceof Array) ? | 
					
						
							|  |  |  | 					title | 
					
						
							| 
									
										
										
										
											2020-11-25 16:36:42 +03:00
										 |  |  | 					: [title]) | 
					
						
							| 
									
										
										
										
											2020-11-25 16:34:15 +03:00
										 |  |  | 			.send('all', ...args) }, | 
					
						
							| 
									
										
										
										
											2020-11-25 23:57:46 +03:00
										 |  |  | 	// XXX should this be an event???
 | 
					
						
							|  |  |  | 	//		the best way to go would be to proxy this to task-specific
 | 
					
						
							|  |  |  | 	//		variants and register handlers on the tasks...
 | 
					
						
							|  |  |  | 	//		...should work with .on(..) / ... and other event methods...
 | 
					
						
							|  |  |  | 	stop: function(title='all'){ | 
					
						
							|  |  |  | 		this.send(title, 'stop')  | 
					
						
							|  |  |  | 		return this }, | 
					
						
							| 
									
										
										
										
											2020-12-16 03:50:59 +03:00
										 |  |  | 	// XXX
 | 
					
						
							|  |  |  | 	abort: function(title='all'){ | 
					
						
							|  |  |  | 		this.send(title, 'abort')  | 
					
						
							|  |  |  | 		return this },   | 
					
						
							| 
									
										
										
										
											2020-11-25 15:15:12 +03:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-11-25 20:39:55 +03:00
										 |  |  | 	// events...
 | 
					
						
							|  |  |  | 	//
 | 
					
						
							| 
									
										
										
										
											2020-11-26 22:56:23 +03:00
										 |  |  | 	// XXX need to be able to bind to proxy events...
 | 
					
						
							| 
									
										
										
										
											2020-11-25 20:39:55 +03:00
										 |  |  | 	done: events.PureEvent('done'), | 
					
						
							|  |  |  | 	error: events.PureEvent('error'), | 
					
						
							| 
									
										
										
										
											2020-11-25 23:57:46 +03:00
										 |  |  | 	tasksDone: events.PureEvent('tasksDone'), | 
					
						
							| 
									
										
										
										
											2020-11-24 05:48:54 +03:00
										 |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-11-27 18:18:07 +03:00
										 |  |  | 	// Create/start a task...
 | 
					
						
							| 
									
										
										
										
											2020-11-25 14:54:27 +03:00
										 |  |  | 	//
 | 
					
						
							| 
									
										
										
										
											2020-11-25 16:34:15 +03:00
										 |  |  | 	//	Create a task...
 | 
					
						
							|  |  |  | 	//	.Task(task)
 | 
					
						
							|  |  |  | 	//	.Task(title, task)
 | 
					
						
							| 
									
										
										
										
											2020-11-25 14:54:27 +03:00
										 |  |  | 	//		-> task-handler
 | 
					
						
							|  |  |  | 	//
 | 
					
						
							| 
									
										
										
										
											2020-11-26 22:56:23 +03:00
										 |  |  | 	//
 | 
					
						
							| 
									
										
										
										
											2020-11-25 16:34:15 +03:00
										 |  |  | 	//	Create a function task...
 | 
					
						
							|  |  |  | 	//	.Task(func, ..)
 | 
					
						
							|  |  |  | 	//	.Task(title, func, ..)
 | 
					
						
							|  |  |  | 	//		-> task-handler
 | 
					
						
							|  |  |  | 	//
 | 
					
						
							| 
									
										
										
										
											2020-11-26 22:56:23 +03:00
										 |  |  | 	//	func(ticket, ..)
 | 
					
						
							| 
									
										
										
										
											2020-11-25 16:34:15 +03:00
										 |  |  | 	//
 | 
					
						
							|  |  |  | 	//
 | 
					
						
							|  |  |  | 	// A task can be:
 | 
					
						
							|  |  |  | 	// 	- Promise.cooperative instance
 | 
					
						
							|  |  |  | 	// 	- Queue instance
 | 
					
						
							|  |  |  | 	// 	- function
 | 
					
						
							|  |  |  | 	// 	- Promise instance
 | 
					
						
							|  |  |  | 	//
 | 
					
						
							| 
									
										
										
										
											2020-11-26 22:56:23 +03:00
										 |  |  | 	// The task-manager is a Promise.interactive(..) instance with 
 | 
					
						
							|  |  |  | 	// TaskMixin added.
 | 
					
						
							|  |  |  | 	//
 | 
					
						
							|  |  |  | 	// The ticket is a TaskTicket instance, see it for reference...
 | 
					
						
							|  |  |  | 	//
 | 
					
						
							| 
									
										
										
										
											2020-11-25 16:34:15 +03:00
										 |  |  | 	//
 | 
					
						
							| 
									
										
										
										
											2020-11-27 18:18:07 +03:00
										 |  |  | 	//
 | 
					
						
							|  |  |  | 	// We can also force a specific task to start sync/async regardless 
 | 
					
						
							|  |  |  | 	// of the .sync_start setting:
 | 
					
						
							|  |  |  | 	//
 | 
					
						
							|  |  |  | 	//	.Task('sync', task)
 | 
					
						
							|  |  |  | 	//	.Task('sync', title, task)
 | 
					
						
							|  |  |  | 	//	.Task(title, 'sync', task)
 | 
					
						
							|  |  |  | 	//		-> task-handler
 | 
					
						
							|  |  |  | 	//
 | 
					
						
							|  |  |  | 	//	.Task('async', task)
 | 
					
						
							|  |  |  | 	//	.Task('async', title, task)
 | 
					
						
							|  |  |  | 	//	.Task(title, 'async', task)
 | 
					
						
							|  |  |  | 	//		-> task-handler
 | 
					
						
							|  |  |  | 	//
 | 
					
						
							|  |  |  | 	//
 | 
					
						
							|  |  |  | 	// sync/async start mode apply only to function tasks and tasks that
 | 
					
						
							|  |  |  | 	// have a .start() method like Queue's...
 | 
					
						
							|  |  |  | 	//
 | 
					
						
							|  |  |  | 	//
 | 
					
						
							|  |  |  | 	// NOTE: 'sync' more for a blocking task will block the task manager.
 | 
					
						
							| 
									
										
										
										
											2020-11-26 22:56:23 +03:00
										 |  |  | 	// NOTE: only function tasks accept args.
 | 
					
						
							| 
									
										
										
										
											2020-11-25 04:04:44 +03:00
										 |  |  | 	// NOTE: the task is started as soon as it is accepted.
 | 
					
						
							| 
									
										
										
										
											2020-11-26 22:56:23 +03:00
										 |  |  | 	// NOTE: tasks trigger events only on the task-manager instance that
 | 
					
						
							|  |  |  | 	// 		they were created in...
 | 
					
						
							|  |  |  | 	// 		XXX try and make all event handlers to be registered in the 
 | 
					
						
							|  |  |  | 	// 			task itself and all the manager events just be proxies 
 | 
					
						
							|  |  |  | 	// 			to tasks...
 | 
					
						
							|  |  |  | 	// 			...this needs to work with all the event methods...
 | 
					
						
							| 
									
										
										
										
											2020-11-25 16:34:15 +03:00
										 |  |  | 	Task: function(title, task, ...args){ | 
					
						
							| 
									
										
										
										
											2020-11-24 05:48:54 +03:00
										 |  |  | 		var that = this | 
					
						
							| 
									
										
										
										
											2020-11-27 01:53:14 +03:00
										 |  |  | 		var _args = [...arguments] | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		// parse args...
 | 
					
						
							|  |  |  | 		var sync_start = this.sync_start | 
					
						
							|  |  |  | 		if(title == 'sync' || title == 'async'){ | 
					
						
							|  |  |  | 			;[sync_start, title, task, ...args] = _args | 
					
						
							|  |  |  | 			sync_start = sync_start == 'sync' | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		} else if(task == 'sync' || task == 'async'){ | 
					
						
							|  |  |  | 			;[title, sync_start, task, ...args] = _args | 
					
						
							|  |  |  | 			sync_start = sync_start == 'sync' } | 
					
						
							| 
									
										
										
										
											2020-11-24 05:48:54 +03:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-11-25 14:54:27 +03:00
										 |  |  | 		// anonymous task...
 | 
					
						
							| 
									
										
										
										
											2020-11-25 16:34:15 +03:00
										 |  |  | 		if(typeof(title) != typeof('str')){ | 
					
						
							| 
									
										
										
										
											2020-11-27 01:53:14 +03:00
										 |  |  | 			;[task, ...args] = _args | 
					
						
							| 
									
										
										
										
											2020-11-25 16:34:15 +03:00
										 |  |  | 			title = null } | 
					
						
							| 
									
										
										
										
											2020-11-25 04:04:44 +03:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-11-24 05:48:54 +03:00
										 |  |  | 		// normalize handler...
 | 
					
						
							| 
									
										
										
										
											2020-11-25 04:04:44 +03:00
										 |  |  | 		var run | 
					
						
							| 
									
										
										
										
											2020-11-24 05:48:54 +03:00
										 |  |  | 		var handler =  | 
					
						
							|  |  |  | 			// queue...
 | 
					
						
							|  |  |  | 			// NOTE: queue is task-compatible...
 | 
					
						
							| 
									
										
										
										
											2020-12-16 05:17:27 +03:00
										 |  |  | 			task instanceof FinalizableQueue ? | 
					
						
							| 
									
										
										
										
											2020-11-25 04:04:44 +03:00
										 |  |  | 				task | 
					
						
							| 
									
										
										
										
											2020-11-25 14:54:27 +03:00
										 |  |  | 			// task protocol...
 | 
					
						
							| 
									
										
										
										
											2020-12-03 17:53:25 +03:00
										 |  |  | 			: task && task.then  | 
					
						
							|  |  |  | 					&& (task.stop || task.send) ? | 
					
						
							| 
									
										
										
										
											2020-11-24 05:48:54 +03:00
										 |  |  | 				task | 
					
						
							| 
									
										
										
										
											2020-11-25 23:57:46 +03:00
										 |  |  | 			: this.__task_mixin__( | 
					
						
							| 
									
										
										
										
											2020-11-25 14:54:27 +03:00
										 |  |  | 				// interactive promise...
 | 
					
						
							|  |  |  | 				task instanceof Promise.interactive ? | 
					
						
							|  |  |  | 					task | 
					
						
							| 
									
										
										
										
											2020-11-24 05:48:54 +03:00
										 |  |  | 				// dumb promise -- will ignore all the messages...
 | 
					
						
							|  |  |  | 				// XXX should we complain about this???
 | 
					
						
							| 
									
										
										
										
											2020-11-25 14:54:27 +03:00
										 |  |  | 				: task instanceof Promise ? | 
					
						
							| 
									
										
										
										
											2020-11-24 05:48:54 +03:00
										 |  |  | 					Promise.interactive( | 
					
						
							|  |  |  | 						function(resolve, reject, onmsg){ | 
					
						
							| 
									
										
										
										
											2020-11-25 16:34:15 +03:00
										 |  |  | 							// NOTE: since this is a promise, we can't
 | 
					
						
							|  |  |  | 							// 		stop it externally...
 | 
					
						
							| 
									
										
										
										
											2020-11-25 15:15:12 +03:00
										 |  |  | 							onmsg(function(msg){ | 
					
						
							|  |  |  | 								msg == 'stop' | 
					
						
							| 
									
										
										
										
											2020-11-25 16:34:15 +03:00
										 |  |  | 									&& reject('stop') }) | 
					
						
							| 
									
										
										
										
											2020-11-24 05:48:54 +03:00
										 |  |  | 							task.then(resolve, reject) }) | 
					
						
							|  |  |  | 				// function...
 | 
					
						
							|  |  |  | 				: Promise.interactive( | 
					
						
							| 
									
										
										
										
											2020-11-25 16:34:15 +03:00
										 |  |  | 					function(resolve, reject, onmessage){ | 
					
						
							|  |  |  | 						// NOTE: we need to start this a bit later hence 
 | 
					
						
							|  |  |  | 						// 		we wrap this into run(..) and call it when
 | 
					
						
							|  |  |  | 						// 		the context is ready...
 | 
					
						
							| 
									
										
										
										
											2020-11-25 04:04:44 +03:00
										 |  |  | 						run = function(){ | 
					
						
							| 
									
										
										
										
											2020-11-25 17:22:25 +03:00
										 |  |  | 							var res =  | 
					
						
							| 
									
										
										
										
											2020-11-25 20:39:55 +03:00
										 |  |  | 								task( | 
					
						
							| 
									
										
										
										
											2020-11-26 23:28:29 +03:00
										 |  |  | 									that.__task_ticket__(title, resolve, reject, onmessage, handler),  | 
					
						
							| 
									
										
										
										
											2020-11-25 20:39:55 +03:00
										 |  |  | 									...args)  | 
					
						
							| 
									
										
										
										
											2020-11-25 16:58:05 +03:00
										 |  |  | 							// NOTE: if the client calls resolve(..) this 
 | 
					
						
							|  |  |  | 							// 		second resolve(..) call has no effect,
 | 
					
						
							|  |  |  | 							// 		and the same is true with reject...
 | 
					
						
							|  |  |  | 							// XXX is double binding like this (i.e. 
 | 
					
						
							|  |  |  | 							// 		ticket + .then()) a good idea???
 | 
					
						
							|  |  |  | 							res instanceof Promise | 
					
						
							| 
									
										
										
										
											2020-11-25 17:22:25 +03:00
										 |  |  | 								&& res.then(resolve, reject) } })) | 
					
						
							| 
									
										
										
										
											2020-11-25 16:34:15 +03:00
										 |  |  | 		// set handler title...
 | 
					
						
							|  |  |  | 		// NOTE: this will override the title of the handler if it was 
 | 
					
						
							| 
									
										
										
										
											2020-11-25 14:54:27 +03:00
										 |  |  | 		// 		set before...
 | 
					
						
							| 
									
										
										
										
											2020-11-25 16:34:15 +03:00
										 |  |  | 		if(title){ | 
					
						
							|  |  |  | 			handler.title | 
					
						
							| 
									
										
										
										
											2020-11-25 14:54:27 +03:00
										 |  |  | 				&& console.warn( | 
					
						
							| 
									
										
										
										
											2020-11-25 16:34:15 +03:00
										 |  |  | 					'TaskManager.Task(..): task title already defined:', handler.title, | 
					
						
							|  |  |  | 					'overwriting with:', title) | 
					
						
							| 
									
										
										
										
											2020-11-25 16:58:05 +03:00
										 |  |  | 			Object.assign(handler, {title}) } | 
					
						
							| 
									
										
										
										
											2020-11-24 05:48:54 +03:00
										 |  |  | 
 | 
					
						
							|  |  |  | 		this.push(handler) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-11-28 03:47:31 +03:00
										 |  |  | 		this.record_times | 
					
						
							|  |  |  | 			&& (handler.time_started = Date.now()) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-11-25 17:22:25 +03:00
										 |  |  | 		// handle task manager state...
 | 
					
						
							|  |  |  | 		var cleanup = function(evt){ | 
					
						
							|  |  |  | 			return function(res){ | 
					
						
							| 
									
										
										
										
											2020-11-28 00:22:27 +03:00
										 |  |  | 				that.record_times | 
					
						
							|  |  |  | 					&& (handler.time_ended = Date.now()) | 
					
						
							| 
									
										
										
										
											2020-11-25 17:22:25 +03:00
										 |  |  | 				that.splice(that.indexOf(handler), 1) | 
					
						
							|  |  |  | 				that.trigger(evt, task, res)  | 
					
						
							| 
									
										
										
										
											2020-11-25 23:57:46 +03:00
										 |  |  | 				that.length == 0 | 
					
						
							|  |  |  | 					&& that.trigger('tasksDone') } } | 
					
						
							| 
									
										
										
										
											2020-11-25 15:15:12 +03:00
										 |  |  | 		handler | 
					
						
							| 
									
										
										
										
											2020-11-25 17:22:25 +03:00
										 |  |  | 			.then(cleanup('done'), cleanup('error')) | 
					
						
							| 
									
										
										
										
											2020-11-24 05:48:54 +03:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-11-25 17:22:25 +03:00
										 |  |  | 		// start...
 | 
					
						
							| 
									
										
										
										
											2020-11-25 04:04:44 +03:00
										 |  |  | 		var start = function(){ | 
					
						
							| 
									
										
										
										
											2020-11-25 14:54:27 +03:00
										 |  |  | 			run ? | 
					
						
							|  |  |  | 				run() | 
					
						
							|  |  |  | 			: task.start ? | 
					
						
							|  |  |  | 				task.start() | 
					
						
							|  |  |  | 	   		: null } | 
					
						
							| 
									
										
										
										
											2020-11-25 17:22:25 +03:00
										 |  |  | 		// trigger task start...
 | 
					
						
							| 
									
										
										
										
											2020-11-27 01:53:14 +03:00
										 |  |  | 		sync_start ? | 
					
						
							| 
									
										
										
										
											2020-11-25 04:04:44 +03:00
										 |  |  | 			start() | 
					
						
							|  |  |  | 			: setTimeout(start, 0) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-11-24 05:48:54 +03:00
										 |  |  | 		return handler }, | 
					
						
							|  |  |  | })) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-11-07 02:49:32 +03:00
										 |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /********************************************************************** | 
					
						
							|  |  |  | * vim:set ts=4 sw=4 :                               */ return module }) |