mirror of
https://github.com/flynx/types.js.git
synced 2025-12-19 18:01:39 +00:00
experimentng with task manager concept + refactoring...
Signed-off-by: Alex A. Naanou <alex.nanou@gmail.com>
This commit is contained in:
parent
ea6fbc1ff5
commit
ee91cd2601
148
Promise.js
148
Promise.js
@ -13,7 +13,6 @@ var object = require('ig-object')
|
||||
|
||||
/*********************************************************************/
|
||||
|
||||
// XXX should this be aborted on reject???
|
||||
var IterablePromise =
|
||||
module.IterablePromise =
|
||||
object.Constructor('IterablePromise', Promise, {
|
||||
@ -95,10 +94,10 @@ object.Constructor('IterablePromise', Promise, {
|
||||
//
|
||||
// NOTE: .catch(..) and .finally(..) are implemented through .then(..)
|
||||
// so we do not need to overload those...
|
||||
then: function(onfulfilled, onrejected){
|
||||
then: function (onfulfilled, onrejected){
|
||||
return new Promise(
|
||||
function(resolve, reject){
|
||||
object.parentCall(IterablePromise.prototype.then, this,
|
||||
Promise.prototype.then.call(this,
|
||||
// NOTE: resolve(..) / reject(..) return undefined so
|
||||
// we can't pass them directly here...
|
||||
function(res){
|
||||
@ -108,7 +107,7 @@ object.Constructor('IterablePromise', Promise, {
|
||||
reject(res)
|
||||
return res }) }.bind(this))
|
||||
.then(...arguments) },
|
||||
|
||||
|
||||
|
||||
//
|
||||
// Promise.iter([ .. ])
|
||||
@ -145,8 +144,9 @@ object.Constructor('IterablePromise', Promise, {
|
||||
var promise
|
||||
|
||||
// instance...
|
||||
var obj = Reflect.construct(IterablePromise.__proto__, [
|
||||
function(resolve, reject){
|
||||
var obj = Reflect.construct(
|
||||
IterablePromise.__proto__,
|
||||
[function(resolve, reject){
|
||||
// NOTE: this is here for Promise compatibilty...
|
||||
if(typeof(list) == 'function'){
|
||||
return list.call(this, ...arguments) }
|
||||
@ -202,48 +202,112 @@ object.Constructor('IterablePromise', Promise, {
|
||||
|
||||
|
||||
|
||||
//---------------------------------------------------------------------
|
||||
|
||||
var InteractivePromise =
|
||||
module.InteractivePromise =
|
||||
object.Constructor('InteractivePromise', Promise, {
|
||||
__message_handlers: null,
|
||||
|
||||
send: function(...args){
|
||||
var that = this
|
||||
;(this.__message_handlers || [])
|
||||
.forEach(function(h){ h.call(that, ...args) })
|
||||
return this },
|
||||
|
||||
then: IterablePromise.prototype.then,
|
||||
|
||||
//
|
||||
// Promise.interactive(handler)
|
||||
// -> interacive-promise
|
||||
//
|
||||
// handler(resolve, reject, onmessage)
|
||||
//
|
||||
// onmessage(func)
|
||||
//
|
||||
//
|
||||
__new__: function(_, handler){
|
||||
var handlers = []
|
||||
var onmessage = function(func){
|
||||
handlers.push(func) }
|
||||
|
||||
var obj = Reflect.construct(
|
||||
InteractivePromise.__proto__,
|
||||
!handler ?
|
||||
[]
|
||||
: [function(resolve, reject){
|
||||
return handler(resolve, reject, onmessage) }],
|
||||
InteractivePromise)
|
||||
|
||||
Object.defineProperty(obj, '__message_handlers', {
|
||||
value: handlers,
|
||||
enumerable: false,
|
||||
})
|
||||
return obj },
|
||||
})
|
||||
|
||||
|
||||
|
||||
//---------------------------------------------------------------------
|
||||
|
||||
var CooperativePromise =
|
||||
module.CooperativePromise =
|
||||
object.Constructor('CooperativePromise', Promise, {
|
||||
__handlers: null,
|
||||
|
||||
get isSet(){
|
||||
return this.__handlers === false },
|
||||
|
||||
set: function(value, resolve=true){
|
||||
// can't set twice...
|
||||
if(this.isSet){
|
||||
throw new Error('.set(..): can not set twice') }
|
||||
// bind to promise...
|
||||
if(value && value.then && value.catch){
|
||||
value.then(handlers.resolve)
|
||||
value.catch(handlers.reject)
|
||||
// resolve with value...
|
||||
} else {
|
||||
resolve ?
|
||||
this.__handlers.resolve(value)
|
||||
: this.__handlers.reject(value) }
|
||||
// cleanup and prevent setting twice...
|
||||
this.__handlers = false
|
||||
return this },
|
||||
|
||||
then: IterablePromise.prototype.then,
|
||||
|
||||
__new__: function(){
|
||||
var handlers
|
||||
var resolver = arguments[1]
|
||||
|
||||
var obj = Reflect.construct(
|
||||
CooperativePromise.__proto__,
|
||||
[function(resolve, reject){
|
||||
handlers = {resolve, reject}
|
||||
// NOTE: this is here to support builtin .then(..)
|
||||
resolver
|
||||
&& resolver(resolve, reject) }],
|
||||
CooperativePromise)
|
||||
|
||||
Object.defineProperty(obj, '__handlers', {
|
||||
value: handlers,
|
||||
enumerable: false,
|
||||
writable: true,
|
||||
})
|
||||
return obj },
|
||||
})
|
||||
|
||||
|
||||
|
||||
//---------------------------------------------------------------------
|
||||
|
||||
var PromiseMixin =
|
||||
module.PromiseMixin =
|
||||
object.Mixin('PromiseMixin', 'soft', {
|
||||
// XXX does this need to be a distinct object/constructor???
|
||||
cooperative: function(){
|
||||
var handlers
|
||||
return object.mixinFlat(
|
||||
new Promise(function(resolve, reject){
|
||||
handlers = { resolve, reject, } }),
|
||||
{
|
||||
get isSet(){
|
||||
return handlers === false },
|
||||
//
|
||||
// Resolve promise with value...
|
||||
// .set(value)
|
||||
// -> this
|
||||
//
|
||||
// Reject promise with value...
|
||||
// .set(value, false)
|
||||
// -> this
|
||||
//
|
||||
set: function(value, resolve=true){
|
||||
// can't set twice...
|
||||
if(this.isSet){
|
||||
throw new Error('Promise.cooperative().set(..): can not set twice') }
|
||||
// bind to promise...
|
||||
if(value && value.then && value.catch){
|
||||
value.then(handlers.resolve)
|
||||
value.catch(handlers.reject)
|
||||
// resolve with value...
|
||||
} else {
|
||||
resolve ?
|
||||
handlers.resolve(value)
|
||||
: handlers.reject(value) }
|
||||
// cleanup and prevent setting twice...
|
||||
handlers = false
|
||||
return this },
|
||||
}) },
|
||||
|
||||
iter: IterablePromise,
|
||||
interactive: InteractivePromise,
|
||||
cooperative: CooperativePromise,
|
||||
})
|
||||
|
||||
|
||||
|
||||
43
event.js
43
event.js
@ -46,14 +46,13 @@ var Eventfull =
|
||||
module.Eventfull =
|
||||
function(name, func, options={}){
|
||||
var hidden
|
||||
var method
|
||||
|
||||
options = func && typeof(func) != 'function' ?
|
||||
func
|
||||
: options
|
||||
|
||||
return object.mixinFlat(
|
||||
method = function(...args){
|
||||
var method = object.mixin(
|
||||
function(...args){
|
||||
var handlers =
|
||||
// hidden...
|
||||
options.handlerLocation == 'hidden' ?
|
||||
@ -107,8 +106,14 @@ function(name, func, options={}){
|
||||
: options.handlerLocation == 'method' ?
|
||||
(method.__event_handlers__ = method.__event_handlers__ || [])
|
||||
// context (default)...
|
||||
: ((context.__event_handlers__ = context.__event_handlers__ || {})[name] =
|
||||
context.__event_handlers__[name] || [])
|
||||
: (context.__event_handlers__ == null ?
|
||||
Object.defineProperty(context, '__event_handlers__', {
|
||||
value: {[name]: (handlers = [])},
|
||||
enumerable: false,
|
||||
})
|
||||
&& handlers
|
||||
: (context.__event_handlers__[name] =
|
||||
context.__event_handlers__[name] || []))
|
||||
// add handler...
|
||||
handlers.push(func)
|
||||
return this },
|
||||
@ -128,7 +133,13 @@ function(name, func, options={}){
|
||||
toString: function(){
|
||||
return func.toString()
|
||||
.replace(/^(function[^(]*\()[^,)]*, ?/, '$1') },
|
||||
}) }
|
||||
})
|
||||
|
||||
Object.defineProperty(method, 'name', {
|
||||
value: name,
|
||||
})
|
||||
|
||||
return method }
|
||||
|
||||
|
||||
module.TRIGGER = {doc: 'force event method to trigger'}
|
||||
@ -169,6 +180,8 @@ module.TRIGGER = {doc: 'force event method to trigger'}
|
||||
// arg is a function or not...
|
||||
//
|
||||
//
|
||||
// XXX might be a good idea to adde an event that can't be triggered by
|
||||
// calling...
|
||||
var Event =
|
||||
module.Event =
|
||||
function(name, func, options={}){
|
||||
@ -177,7 +190,8 @@ function(name, func, options={}){
|
||||
func
|
||||
: options
|
||||
|
||||
return Object.assign(
|
||||
//return Object.assign(
|
||||
return object.mixin(
|
||||
method = Eventfull(name,
|
||||
function(handle, ...args){
|
||||
// add handler...
|
||||
@ -199,8 +213,10 @@ function(name, func, options={}){
|
||||
// NOTE: this is a copy of Eventfull's .toString() as we
|
||||
// still need to base the doc on the user's func...
|
||||
toString: function(){
|
||||
return func.toString()
|
||||
.replace(/^(function[^(]*\()[^,)]*, ?/, '$1') },
|
||||
return func ?
|
||||
func.toString()
|
||||
.replace(/^(function[^(]*\()[^,)]*, ?/, '$1')
|
||||
: `function ${name}(){}`},
|
||||
}) }
|
||||
|
||||
|
||||
@ -213,7 +229,7 @@ function(name, func, options={}){
|
||||
// XXX do we need to be able to force global handler???
|
||||
var EventHandlerMixin =
|
||||
module.EventHandlerMixin = object.Mixin('EventHandlerMixin', {
|
||||
__event_handlers__: null,
|
||||
//__event_handlers__: null,
|
||||
|
||||
on: function(evt, func){
|
||||
// event...
|
||||
@ -222,7 +238,12 @@ module.EventHandlerMixin = object.Mixin('EventHandlerMixin', {
|
||||
this[evt].__event_handler_add__(this, func)
|
||||
// non-event...
|
||||
} else {
|
||||
;((this.__event_handlers__ = this.__event_handlers__ || {})[evt] =
|
||||
this.__event_handlers__ == null
|
||||
&& Object.defineProperty(this, '__event_handlers__', {
|
||||
value: {},
|
||||
enumerable: false,
|
||||
})
|
||||
;(this.__event_handlers__[evt] =
|
||||
this.__event_handlers__[evt] || [])
|
||||
.push(func) }
|
||||
return this },
|
||||
|
||||
8
package-lock.json
generated
8
package-lock.json
generated
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "ig-types",
|
||||
"version": "3.7.14",
|
||||
"version": "5.0.0",
|
||||
"lockfileVersion": 1,
|
||||
"requires": true,
|
||||
"dependencies": {
|
||||
@ -265,9 +265,9 @@
|
||||
}
|
||||
},
|
||||
"ig-object": {
|
||||
"version": "5.4.11",
|
||||
"resolved": "https://registry.npmjs.org/ig-object/-/ig-object-5.4.11.tgz",
|
||||
"integrity": "sha512-WPPQ5C41c6q3tPfa2fBbWE2xcLF7LoGRu2E6Wr/aoA5oxAyl8lAuE7Kqt4TyPwfW9jVI0+ifBztg9e1tR5mG1Q=="
|
||||
"version": "5.4.12",
|
||||
"resolved": "https://registry.npmjs.org/ig-object/-/ig-object-5.4.12.tgz",
|
||||
"integrity": "sha512-9kZM80Js9/eTwXN9VXwLDC1wDJ7gIAdYU9GIzb5KJmNcLAMaW+zhgFrwFFMrcSfggUuadgnqSrS41E4XLe8JZw=="
|
||||
},
|
||||
"ig-test": {
|
||||
"version": "1.4.8",
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "ig-types",
|
||||
"version": "5.0.0",
|
||||
"version": "5.0.2",
|
||||
"description": "Generic JavaScript types and type extensions...",
|
||||
"main": "main.js",
|
||||
"scripts": {
|
||||
@ -23,7 +23,7 @@
|
||||
},
|
||||
"homepage": "https://github.com/flynx/types.js#readme",
|
||||
"dependencies": {
|
||||
"ig-object": "^5.4.11",
|
||||
"ig-object": "^5.4.12",
|
||||
"object-run": "^1.0.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
|
||||
151
runner.js
151
runner.js
@ -21,6 +21,7 @@
|
||||
var object = require('ig-object')
|
||||
|
||||
require('./Array')
|
||||
require('./Promise')
|
||||
|
||||
var events = require('./event')
|
||||
|
||||
@ -38,7 +39,8 @@ module.STOP = object.STOP
|
||||
// XXX do we need an async mode -- exec .__run_tasks__(..) in a
|
||||
// setTimeout(.., 0)???
|
||||
var Queue =
|
||||
module.Queue = object.Constructor('Queue', Array, {
|
||||
module.Queue =
|
||||
object.Constructor('Queue', Array, {
|
||||
// create a running queue...
|
||||
runTasks: function(...tasks){
|
||||
return this({ state: 'running' }, ...tasks) },
|
||||
@ -106,6 +108,16 @@ module.Queue = object.Constructor('Queue', Array, {
|
||||
queueEmpty: events.Event('queueEmpty'),
|
||||
|
||||
|
||||
// NOTE: each handler will get called once when the next time the
|
||||
// queue is emptied...
|
||||
// XXX revise...
|
||||
then: function(func){
|
||||
var that = this
|
||||
return new Promise(function(resolve, reject){
|
||||
that.one('queueEmpty', function(){
|
||||
resolve(func()) }) }) },
|
||||
|
||||
|
||||
// helpers...
|
||||
//
|
||||
// move tasks to head/tail of queue resp.
|
||||
@ -142,7 +154,7 @@ module.Queue = object.Constructor('Queue', Array, {
|
||||
task.start()
|
||||
: task },
|
||||
//
|
||||
// Hanlde 'running' state...
|
||||
// Hanlde 'running' state (async)...
|
||||
// .__run_tasks__()
|
||||
// -> this
|
||||
//
|
||||
@ -152,36 +164,33 @@ module.Queue = object.Constructor('Queue', Array, {
|
||||
__running: null,
|
||||
__run_tasks__: function(){
|
||||
var that = this
|
||||
this.state == 'running'
|
||||
&& setTimeout(function(){
|
||||
// handle queue...
|
||||
while(this.length > 0
|
||||
&& this.state == 'running'
|
||||
&& (this.__running || []).length < (this.pool_size || Infinity) ){
|
||||
this.runTask(this.__run_tasks__.bind(this)) }
|
||||
|
||||
// if we are not running stop immidiately...
|
||||
if(this.state != 'running'){
|
||||
return this }
|
||||
|
||||
// handle queue...
|
||||
while(this.length > 0
|
||||
&& this.state == 'running'
|
||||
&& (this.__running || []).length < (this.pool_size || Infinity) ){
|
||||
this.runTask(this.__run_tasks__.bind(this)) }
|
||||
|
||||
// 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...
|
||||
//
|
||||
// XXX will this be collected by the GC if it is polling???
|
||||
if(this.length == 0
|
||||
&& this.state == 'running'){
|
||||
this.auto_stop ?
|
||||
// auto-stop...
|
||||
this.stop()
|
||||
// pole...
|
||||
: (this.poling_delay
|
||||
&& setTimeout(
|
||||
this.__run_tasks__.bind(this),
|
||||
this.poling_delay || 200)) }
|
||||
// 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...
|
||||
//
|
||||
// XXX will this be collected by the GC if it is polling???
|
||||
if(this.length == 0
|
||||
&& this.state == 'running'){
|
||||
this.auto_stop ?
|
||||
// auto-stop...
|
||||
this.stop()
|
||||
// pole...
|
||||
: (this.poling_delay
|
||||
&& setTimeout(
|
||||
this.__run_tasks__.bind(this),
|
||||
this.poling_delay || 200)) } }.bind(this), 0)
|
||||
return this },
|
||||
|
||||
// run one task from queue...
|
||||
@ -291,6 +300,86 @@ module.Queue = object.Constructor('Queue', Array, {
|
||||
}))
|
||||
|
||||
|
||||
//---------------------------------------------------------------------
|
||||
// Task manager...
|
||||
//
|
||||
// goal:
|
||||
// externally manage long running functions/promises/etc
|
||||
//
|
||||
// enteties:
|
||||
// task
|
||||
// - wrap a function/promise
|
||||
// - pass the function/promise a reciver
|
||||
// - return a controller (store in manager)
|
||||
// manager
|
||||
// - container for tasks
|
||||
// - multiplex actions to tasks
|
||||
//
|
||||
|
||||
|
||||
var TaskMixin =
|
||||
object.Mixin('TaskMixin', 'soft', {
|
||||
stop: function(){
|
||||
this.send('stop', ...arguments) },
|
||||
})
|
||||
|
||||
|
||||
// XXX should this be a Queue???
|
||||
var TaskManager =
|
||||
module.TaskManager =
|
||||
object.Constructor('TaskManager', Array, events.EventMixin('flat', {
|
||||
|
||||
// XXX each task should also trigger this when stopping and this
|
||||
// should not result in this and tasks infinitely playing
|
||||
// ping-pong...
|
||||
stop: events.Event('stop',
|
||||
function(task='all'){
|
||||
this.forEach(function(task){
|
||||
;(task == 'all'
|
||||
|| task == '*'
|
||||
|| task === task)
|
||||
&& task.stop() }) }),
|
||||
done: events.Event('done'),
|
||||
|
||||
|
||||
Task: function(task, ...args){
|
||||
var that = this
|
||||
|
||||
// normalize handler...
|
||||
var handler =
|
||||
// queue...
|
||||
// NOTE: queue is task-compatible...
|
||||
task instanceof Queue ?
|
||||
task.start()
|
||||
// interactive...
|
||||
: task && task.then && task.stop ?
|
||||
task
|
||||
: TaskMixin(
|
||||
// dumb promise -- will ignore all the messages...
|
||||
// XXX should we complain about this???
|
||||
task instanceof Promise ?
|
||||
Promise.interactive(
|
||||
function(resolve, reject, onmsg){
|
||||
task.then(resolve, reject) })
|
||||
// function...
|
||||
: Promise.interactive(
|
||||
function(resolve, reject, onmsg){
|
||||
resolve(task(onmsg, ...args)) }))
|
||||
|
||||
this.push(handler)
|
||||
|
||||
// handle task done...
|
||||
handler
|
||||
.then(function(res){
|
||||
that.splice(that.indexOf(handler), 1)
|
||||
that.trigger('done', task, res)
|
||||
that.length == 0
|
||||
&& that.done('all') })
|
||||
|
||||
// XXX or should we return this???
|
||||
return handler },
|
||||
}))
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user