Last active
April 18, 2020 16:47
-
-
Save sketchpunk/fb8beb7b5b3fdfeb8534f2d9907eee03 to your computer and use it in GitHub Desktop.
Event Manager - Handle Instant Event Broadcasting or Queue to Manual Broadcast
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| let e = new EventManager(); | |
| let xxx = (x)=>console.log("x",x); | |
| // EVENT NAME, SIZE, ALLOW_LISTENERS | |
| // Size = 0, Event will broadcast instantly | |
| // Size = 1, Single Item Event Queue, Manaual Broadcast | |
| // Size > 1, Ring Event Queue, Manual Broadcast | |
| e.reg( "test", 1, true ); | |
| // Assign Listeners | |
| e.on( "test", (v)=>console.log("a",v) ); | |
| e.on( "test", (v)=>console.log("b",v) ); | |
| e.on( "test", xxx ); | |
| e.on( "test", xxx ); // Errors, will check if listener already exists. | |
| // Send an Event | |
| e.emit( "test", "woot" ); | |
| // Manually Broadcast Events | |
| //e.broadcast_all(); // Execute All | |
| e.broadcast( "test" ); // Broadcast Specific Event Queue. | |
| class EventManager{ | |
| #items = new Map(); | |
| constructor(){} | |
| // Register Events | |
| reg( evt_name, size=10, allow_listeners=true ){ | |
| if( this.#items.has( evt_name ) ){ console.error(`Event already has been registered : ${evt_name}`); return this; } | |
| let o = { | |
| queue : null, | |
| listeners : (allow_listeners)? new Array() : null, | |
| }; | |
| if( size == 1 ) o.queue = new SingleBuffer(); | |
| else if( size > 1 ) o.queue = new RingBuffer( size ); | |
| this.#items.set( evt_name, o ); | |
| return this; | |
| } | |
| // Assign a Listener to an Event | |
| on( evt_name, func ){ | |
| //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ | |
| // Validation | |
| let o = this.#items.get( evt_name ); | |
| if( !o ){ console.error(`Event does not exist, unable to add listener: ${evt_name}`); return this; } | |
| if( !o.listeners ){ console.error(`Event does not allow listeners: ${evt_name}`); return this; } | |
| let idx = o.listeners.indexOf( func ); | |
| if( idx != -1 ){ console.error(`Listener already exists for event: ${evt_name}`); return this; } | |
| //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ | |
| o.listeners.push( func ); | |
| return this; | |
| } | |
| // Remove a Listener to an Event | |
| off( evt_name, func ){ | |
| //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ | |
| // Validation | |
| let o = this.#items.get( evt_name ); | |
| if( !o ){ console.error(`Event does not exist, unable to add listener: ${evt_name}`); return this; } | |
| if( !o.listeners ){ console.error(`Event does not allow listeners: ${evt_name}`); return this; } | |
| let idx = o.listeners.indexOf( func ); | |
| if( idx == -1 ){ console.error(`Listener not found for event: ${evt_name}`); return this; } | |
| //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ | |
| o.listeners.splice( idx, 1 ); | |
| return this | |
| } | |
| // Send an Event, Depending on the config, it can be sent right away | |
| // or its queued and will be broadcasted at a later date or consumed | |
| // by a System if not allowed listeners. | |
| emit( evt_name, data ){ | |
| //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ | |
| // Validation | |
| let o = this.#items.get( evt_name ); | |
| if( !o ){ console.error(`Event does not exist, unable to add listener: ${evt_name}`); return this; } | |
| //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ | |
| // Instant Broadcast | |
| if( !o.queue && o.listeners ){ | |
| let itm; | |
| for( itm of o.listeners ) itm( data ); | |
| return this; | |
| } | |
| //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ | |
| // Queued | |
| if( o.queue ){ | |
| o.queue.push( data ); | |
| return this; | |
| } | |
| //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ | |
| // Unhandled | |
| console.error(`Event dispatched not handled from emit: ${evt_name}`); | |
| return this; | |
| } | |
| broadcast_all(){ | |
| let data, listener; | |
| for (const [ evt_name, o ] of this.#items.entries()) { | |
| // Listeners Allowed, Listeners Exist, Queue Allowed and Has Data in it. | |
| if( o.listeners && o.listeners.length > 0 && o.queue && o.queue.length() > 0 ){ | |
| while( (data = o.queue.next()) != null ){ // For every Queued Data for the event | |
| for( listener of o.listeners ) listener( data ); // pass it to all the listeners. | |
| } | |
| } | |
| } | |
| return this; | |
| } | |
| broadcast( evt_name ){ | |
| let o = this.#items.get( evt_name ); | |
| if( !o ){ console.error(`Event does not exist, unable to broadcast: ${evt_name}`); return this; } | |
| let data, listener; | |
| if( o.listeners && o.listeners.length > 0 && o.queue && o.queue.length() > 0 ){ | |
| while( (data = o.queue.next()) != null ){ // For every Queued Data for the event | |
| for( listener of o.listeners ) listener( data ); // pass it to all the listeners. | |
| } | |
| } | |
| return true; | |
| } | |
| // Incase a system wants to handle the event queue on its own, it | |
| // can get reference to it and drain it as it sees fit. | |
| get_queue( evt_name ){ | |
| let o = this.#items.get( evt_name ); | |
| return ( o )? o.queue || null : null; | |
| } | |
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| class RingBuffer{ | |
| #buf = null; // Just a Plain Array | |
| #read_idx = 0; // Index to Read on "Next" | |
| #write_idx = 0; // Index to write to on "Push" | |
| #size = 0; // Size of the Buffer | |
| constructor( b_size=10 ){ | |
| this.#buf = new Array( b_size ); | |
| this.#size = b_size; | |
| } | |
| push( v ){ | |
| // Using this "Next Check" method, means we can not fill the buffer to max to start with | |
| // But doing so keeps the index checks very simple. If thats an issues, increase size of buffer. | |
| let next = (this.#write_idx + 1) % this.#size; | |
| if( next == this.#read_idx ){ | |
| console.error( "RingBuffer out of space to write. Write has caught up to Read."); | |
| return false; | |
| } | |
| this.#buf[ this.#write_idx ] = v; | |
| this.#write_idx = next; // Loop Around | |
| return true; | |
| } | |
| next(){ | |
| // If read has caught up to write, then the buffer is empty. | |
| if( this.#read_idx == this.#write_idx ) return null; | |
| let v = this.#buf[ this.#read_idx ]; | |
| this.#read_idx = (this.#read_idx + 1) % this.#size; // Loop Around | |
| return v; | |
| } | |
| length(){ | |
| return ( this.#read_idx == this.#write_idx ) ? 0 : // If Equal, Non Left | |
| ( this.#read_idx < this.#write_idx )? | |
| this.#write_idx - this.#read_idx : // If Read under Write | |
| ( this.#size - this.#read_idx ) + this.#write_idx; // If Write Under Read | |
| } | |
| for_each( func ){ | |
| for( let i = this.#read_idx; i != this.#write_idx; i = (i+1) % this.#size ){ | |
| func( this.#buf[ i ] ); | |
| } | |
| this.#read_idx = 0; | |
| this.#write_idx = 0; | |
| return this; | |
| } | |
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| class SingleBuffer{ | |
| #data = null; | |
| length(){ return ( this.#data == null)? 0:1; } | |
| push( v ){ this.#data = v; return true; } | |
| next(){ | |
| if( this.#data == null ) return null; | |
| let d = this.#data; | |
| this.#data = null; | |
| return d; | |
| } | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment