setTimeout (() => listener (event), 0);With a minimum timeout, everything is fine, the functions of the subscribers are called one after another through a constant minimum interval (sort of like 4mc). But what if we need to increase it, for example, to 500 ms. Then there will simply be a delay for a given interval and then all functions will be called, but also with a minimum interval. And I would like to specify the interval, namely, between calls to subscribers.
constructor(obj){ let instance; /*1*/ if (obj) { instance = Binder.createProto(obj, this); } else { instance = this; } /*2*/ Object.defineProperty(instance, '_binder', { configurable: true, value: {} }); /*3*/ Object.defineProperties(instance._binder, { 'properties': { configurable: true, value: {} }, 'bindings':{ configurable: true, value: {} }, 'watchers':{ configurable: true, value: {} }, 'emitter':{ configurable: true, writable: true, value: {} } }); return instance; } /*1*/ — , , . . `createProto` . /*8*/ /*2*/ — /*3*/ — - `_bunder`, , , . «emitter» , . , (`writable: false`).
/* */ /*4*/ static get timeout(){ return Binder._timeout || 0; } /*5*/ static set timeout(ms){ Object.defineProperty(this, '_timeout', { configurable: true, value: ms }); } /*6*/ static delay(ms = Binder.timeout){ return new Promise((res, rej) => { if(ms > 0){ setTimeout(res, ms); } else { res(); } }); } /*7*/ static get queue(){ return Promise.resolve(); } /*4*/-/*5*/— `_timeout` . , , ES6 -. /*6*/-/*7*/ queue , . `delay` , "" . .
/* */ /*8*/ static createProto(obj, instance){ let className = obj.constructor.name; if(!this.prototypes){ Object.defineProperty(this, 'prototypes', { configurable: true, value: new Map() }); } if(!this.prototypes.has(className)){ let descriptor = { 'constructor': { configurable: true, value: obj.constructor } }; Object.getOwnPropertyNames(instance.__proto__).forEach( ( prop ) => { if(prop !== 'constructor'){ descriptor[prop] = { configurable: true, value: instance[prop] }; } } ); this.prototypes.set( className, Object.create(obj.__proto__, descriptor) ); } obj.__proto__ = this.prototypes.get(className); return obj; } /*8*/— . . , - `Binder.prototypes`
/* */ /*9*/ static transform(obj, prop){ let descriptor, nativeSet; let newGet = function(){ return this._binder.properties[prop];}; let newSet = function(value){ /*10*/ let queues = [Binder.queue, Binder.queue]; /*11*/ if(this._binder.properties[prop] === value){ return; } Object.defineProperty(this._binder.properties, prop, { configurable: true, value: value }); if(this._binder.bindings[prop]){ this._binder.bindings[prop].forEach(( [prop, ms], boundObj ) => { /*12*/ if(boundObj === this._binder.emitter) { this._binder.emitter = null; return; } if(boundObj[prop] === value) return; /*13*/ queues[0] = queues[0] .then(() => Binder.delay(ms) ) .then(() => { boundObj._binder.emitter = obj; boundObj[prop] = value; }); }); queues[0] = queues[0].catch(err => console.log(err) ); } /*14*/ if( this._binder.watchers[prop] ){ this._binder.watchers[prop].forEach( ( [cb, ms] ) => { queues[1] = queues[1] .then(() => Binder.delay(ms) ) .then(() => { cb(value); }); }); } if( this._binder.watchers['*'] ){ this._binder.watchers['*'].forEach( ( [cb, ms] ) => { queues[1] = queues[1] .then(() => Binder.delay(ms) ) .then(() => { cb(value); }); }); } queues[1] = queues[1].catch(err => console.log(err)); }; /*15*/ if(obj.constructor.name.indexOf('HTML') === -1){ descriptor = { configurable: true, enumerable: true, get: newGet, set: newSet }; } else { /*16*/ if('value' in obj) { descriptor = Object.getOwnPropertyDescriptor( obj.constructor.prototype, 'value' ); obj.addEventListener('keydown', function(evob){ if(evob.key.length === 1){ newSet.call(this, this.value + evob.key); } else { Binder.queue.then(() => { newSet.call(this, this.value); }); } }); } else { descriptor = Object.getOwnPropertyDescriptor( Node.prototype, 'textContent' ); } /*17*/ nativeSet = descriptor.set; descriptor.set = function(value){ nativeSet.call(this, value); newSet.call(this, value); }; } Object.defineProperty(obj._binder.properties, prop, { configurable: true, value: obj[prop] }); Object.defineProperty(obj, prop, descriptor); return obj; } /*9*/ - `transform` . JS , `obj._binder.properties`, /. DOM , /. /*10*/ - . /*11*/ - . /*12*/ - - . `obj._binder.emitter` . . . /*13*/ - . /*14*/ - . /*15*/ - DOM /*16*/ - DOM . "" `input`, `textarea` `value` `textContent`. "" / `value` (. `. 2`). , `input` `HTMLInputElementPrototype`. `textContent` / `Node.prototype`(. `. 3`). / `Object.getOwnPropertyDescriptor`. "" . /*17*/ - , . /**/ - `newSet` `newGet`, , .
/*18*/ _bind(ownProp, obj, objProp, ms){ if(!this._binder.bindings[ownProp]) { this._binder.bindings[ownProp] = new Map(); Binder.transform(this, ownProp); } if(this._binder.bindings[ownProp].has(obj)){ return !!console.log('Binding for this object is already set'); } this._binder.bindings[ownProp].set(obj, [objProp, ms]); if( !obj._binder.bindings[objProp] || !obj._binder.bindings[objProp].has(this)) { obj._bind(objProp, this, ownProp, ms); } return this; } /*19*/ _unbind(ownProp, obj, objProp){ try{ this._binder.bindings[ownProp].delete(obj); obj._binder.bindings[objProp].delete(this); return this; } catch(e) { return !!console.log(e); } }; /*20*/ _watch(prop = '*', cb, ms){ var cbHash = Binder.hash(cb.toString().replace(/\s/g,'')); if(!this._binder.watchers[prop]) { this._binder.watchers[prop] = new Map(); if(prop === '*'){ Object.keys(this).forEach( item => { Binder.transform(this, item); }); } else { Binder.transform(this, prop); } } if(this._binder.watchers[prop].has(cbHash)) { return !!console.log('Watchers is already set'); } this._binder.watchers[prop].set(cbHash, [cb, ms]); return cbHash; }; /*21*/ _unwatch(prop = '*', cbHash = 0){ try{ this._binder.watchers[prop].delete(cbHash); return this; } catch(e){ return !!console.log(e); } }; /*18*/ - /*19*/ - /. , , , . () . . `. 6` /*20*/ - /*21*/ - /. ( - "*"). . .
Source: https://habr.com/ru/post/315410/
All Articles