Overriding ES6 class behaviour with traps - node.js

I have recently learned about Proxy objects in javascript, and how you can use a Proxy object to hook into Object.defineProperty for custom behaviour (see this answer if I'm being too vague).
I would like to do the same, but to an entire class so that I can achieve something similar to C#'s INotifyPropertyChanged interface. Essentially, the class will be an EventEmitter that emits data when a property is changed.
class SyncedObject extends EventEmitter {
constructor() {
this.ValueA = 1;
this.ValueB = 2;
}
// Emit this when a property is changed
emitPropertyChanged( eventArgs ) {
// eventArgs.propertyName
// eventArgs.oldValue
// eventArgs.newValue
this.emit( 'propertyChanged', eventArgs );
}
}
Is this something I can do in an ES6 class? I understand Proxy has a get() and set() traps that could achieve this kind of thing, but I'm not sure how to go about writing a class that is automatically wrapped in a Proxy.

After some tinkering, I've been able to more or less achieve what I was hoping to
const EventEmitter = require( 'events' );
class SyncedObject extends EventEmitter {
constructor( obj ) {
super();
this._object = obj;
return new Proxy( this, {
set: ( object, key, value, proxy ) => {
if( this._object[key] === value )
return false;
this._object[key] = value;
if( key[0] === '_') {
var eventArgs = {
propertyName: key,
oldValue: object[key],
newValue: value
};
this.emit( 'propertyChanged', eventArgs );
}
return true;
},
get: ( object, key ) => {
return this._object[key] || object[key];
}
});
}
silentUpdate( data ) {
for(var i = 0, keys = Object.keys(data); i < keys.length; i++)
this._object[keys[i]] = data[keys[i]];
}
}
Not the most bulletproof solution (EventEmitter properties raise the event), but for what I need, it will likely do. If anyone has improvement suggestions, please do post. I'll leave this question open for the time being.

Related

How to prevent that event is emitted from outside?

Imagine I had a simple counting process that acts as a default Node EventEmitter:
import {EventEmitter} from 'events';
async function sleep(milliseconds: number): Promise<void> {
return new Promise((resolve) => setTimeout(resolve, milliseconds));
}
class MyCountingProcess extends EventEmitter {
public async start() {
for (let i = 0; i <= 10; i++) {
this.emit('counting', i);
await sleep(1000); // just some demo async function
}
}
}
const myCountingProcess = new MyCountingProcess();
myCountingProcess.on('counting', (value) => {
console.log("current value is: " + value);
});
myCountingProcess.start();
So far, everything works as intended.
What puzzles me however is that I could very easily disturb this logic by adding another line outside MyCountingProcess (after myCountingProcess.start()) like
myCountingProcess.emit(666);
Then suddenly, the exact same process would no longer work as intended.
Is this intentional? Or bad architectural design? Or am I using the EventEmitter pattern wrong?
It would make much more sense to me if emit could only be called by the EventEmitter (i.e. within it), not on it. Or at least if it could be made a private method in TypeScript, although that would of course only be syntax.
So what would one usually do to prevent scenarios like this one?
Because MyCountingProcess is extending EventEmitter, it will behave as an EventEmmiter instance, unless you override the event methods.
For your use case, I recommend adding the EventEmmiter as a variable inside MyCountingProcess like this:
import { EventEmitter } from 'events';
class MyCountingProcess {
#events: EventEmitter
constructor() {
this.#events = new EventEmitter()
}
public async start() {
for (let i = 0; i <= 10; i++) {
this.#events.emit('counting', i);
await sleep(1000);
}
}
public on(event: string | symbol, listener: (...args: any[]) => void) {
return this.#events.on(event, listener)
}
}
const myCountingProcess = new MyCountingProcess();
myCountingProcess.on('counting', (value) => {
console.log("current value is: " + value);
});
myCountingProcess.start();

Could haxe macro be used to detect when object is dirty (any property has been changed)

Let say we have an object:
#:checkDirty
class Test {
var a:Int;
var b(default, default):String;
var c(get, set):Array<Int>;
public function new() {
...
}
public function get_c() {
...
}
public function set_c(n) {
...
}
}
Could we write a macro checkDirty so that any change to field/properties would set property dirty to true. Macro would generate dirty field as Bool and clearDirty function to set it to false.
var test = new Test();
trace(test.dirty); // false
test.a = 12;
trace(test.dirty); // true
test.clearDirty();
trace(test.dirty); //false
test.b = "test"
trace(test.dirty); //true
test.clearDirty();
test.c = [1,2,3];
trace(test.dirty); //true
Just to note - whenever you consider proxying access to an object, in my experience, there are always hidden costs / added complexity. :)
That said, you have a few approaches:
First, if you want it to be pure Haxe, then either a macro or an abstract can get the job done. Either way, you're effectively transforming every property access into a function call that sets the value and also sets dirty.
For example, an abstract using the #:resolve getter and setter can be found in the NME source code, replicated here for convenience:
#:forward(decode,toString)
abstract URLVariables(URLVariablesBase)
{
public function new(?inEncoded:String)
{
this = new URLVariablesBase(inEncoded);
}
#:resolve
public function set(name:String, value:String) : String
{
return this.set(name,value);
}
#:resolve
public function get(name:String):String
{
return this.get(name);
}
}
This may be an older syntax, I'm not sure... also look at the operator overloading examples on the Haxe manual:
#:op(a.b) public function fieldRead(name:String)
return this.indexOf(name);
#:op(a.b) public function fieldWrite(name:String, value:String)
return this.split(name).join(value);
Second, I'd just point out that if the underlying language / runtime supports some kind of Proxy object (e.g. JavaScript Proxy), and macro / abstract isn't working as expected, then you could build your functionality on top of that.
I wrote a post (archive) about doing this kind of thing (except for emitting events) before - you can use a #:build macro to modify class members, be it appending an extra assignment into setter or replacing the field with a property.
So a modified version might look like so:
class Macro {
public static macro function build():Array<Field> {
var fields = Context.getBuildFields();
for (field in fields.copy()) { // (copy fields so that we don't go over freshly added ones)
switch (field.kind) {
case FVar(fieldType, fieldExpr), FProp("default", "default", fieldType, fieldExpr):
var fieldName = field.name;
if (fieldName == "dirty") continue;
var setterName = "set_" + fieldName;
var tmp_class = macro class {
public var $fieldName(default, set):$fieldType = $fieldExpr;
public function $setterName(v:$fieldType):$fieldType {
$i{fieldName} = v;
this.dirty = true;
return v;
}
};
for (mcf in tmp_class.fields) fields.push(mcf);
fields.remove(field);
case FProp(_, "set", t, e):
var setter = Lambda.find(fields, (f) -> f.name == "set_" + field.name);
if (setter == null) continue;
switch (setter.kind) {
case FFun(f):
f.expr = macro { dirty = true; ${f.expr}; };
default:
}
default:
}
}
if (Lambda.find(fields, (f) -> f.name == "dirty") == null) fields.push((macro class {
public var dirty:Bool = false;
}).fields[0]);
return fields;
}
}
which, if used as
#:build(Macro.build())
#:keep class Some {
public function new() {}
public var one:Int;
public var two(default, set):String;
function set_two(v:String):String {
two = v;
return v;
}
}
Would emit the following JS:
var Some = function() {
this.dirty = false;
};
Some.prototype = {
set_two: function(v) {
this.dirty = true;
this.two = v;
return v;
}
,set_one: function(v) {
this.one = v;
this.dirty = true;
return v;
}
};

How to detect correct type for a variable in Typescript when types are conditional

The code is like this:
class MyClass {
getValue() {
// some code here...
}
}
const IS_ENABLED = process.env.IS_ENABLED || false;
const myClass = IS_ENABLED ? new MyClass() : null;
function getValue() {
if (!IS_ENABLED) return false;
return myClass.getValue();
}
Now at this point, TypeScript is giving error (for myClass.getValue()):
Object is possibly 'null'.
But, since I've checked the condition, I'm sure it's not null.
Is there any way for TypeScript to handle it?
Typescript will not keep track of variables that are related in this way. There are a number of patterns that act as type guards and change the type of a variable.
In this case since IS_ENABLED and myClass are very much related, you can just check the if myClass is undefined.
const IS_ENABLED = process.env.IS_ENABLED || false;
const myClass = IS_ENABLED ? new MyClass() : null;
function getValue() {
if (!myClass) return false;
return myClass.getValue();
}
Or you could use a discriminated union (this might be useful if you have multiple myClass type like varaibles):
const config = (process.env.IS_ENABLED || false) ? {
IS_ENABLED: true as const,
myClass: new MyClass(),
myClass2: new MyClass()
} : {
IS_ENABLED: false as const,
}
function getValue() {
if (!config.IS_ENABLED) return false;
config.myClass2.getValue();
return config.myClass.getValue();
}
Type Guards allow you to narrow down the type of an object within a conditional block.
You can use instanceof to make Typescript knows its type.
const IS_ENABLED = process.env.IS_ENABLED || false;
const myClass = IS_ENABLED ? new MyClass() : null;
function getValue() {
if (!IS_ENABLED) return false;
if (myClass instanceof MyClass) {
return myClass.getValue();
}
return false;
}
For details please check this.

Async function does not return control to the main thread (linq expression)

I thought that I got threads in .NET, but when I have added LINQ expression it made me a little confused.
Like I wrote in the topic of this discussion I dont why the thread doesnt return control to the main action of my controller.
I have written what makes me silly in comments, so let me skip to the true example:
public class ValuesController : ApiController
{
public async Task<List<SomeProduct>> Get()
{
var collection = new List<Mother>() {
new Mother()
{
internalField = new List<Child>()
{
new Child()
{
theLastOne = "VAL"
},
new Child()
{
theLastOne = "VAL"
}
}
}
};
var oss =
from m in collection
from s in m.internalField
select Convert(m, s).Result;
//1-The above code doesnt enter into CONVERT function (I have a breakpoint there)
return oss.ToList();//2- this list enter into COnvertt
}
private async Task<SomeProduct> Convert(Mother ms, Child ss)
{
var ossNEW = new SomeProduct();
await update(ossNEW, ms);
return ossNEW;
}
private async Task update(SomeProduct oss, Mother ms)
{//3 - Naturally it comes here
await Task.Run(()=>
{
//This task is executed (It is example code, pls do not care, that threads do not have any sense
oss.copyOfTheLastOne = ms.internalField.First().theLastOne;
oss.valeFromAnUpdateFunction = "works";
}); //Flow comes here and THIS line does not return control to the main action, why? :)
}
}
public class SomeProduct
{
public string copyOfTheLastOne;
public string valeFromAnUpdateFunction;
}
public class Mother
{
public List<Child> internalField;
}
public class Child
{
public string theLastOne;
}
I have solved this example by adding an "executor", which takes list of the tasks and manage it.
public class ValuesController : ApiController
{
public async Task<List<SomeProduct>> Get()
{
var collection = new List<Mother>() {
new Mother()
{
internalField = new List<Child>()
{
new Child()
{
theLastOne = "VAL"
},
new Child()
{
theLastOne = "VAL"
}
}
}
};
var oss =
from m in collection
from s in m.internalField
select Convert(m, s);
List<Task<SomeProduct>> downloadTasks = oss.ToList();
List<SomeProduct> ossNew = new List<SomeProduct>();
while (downloadTasks.Count > 0)
{
var firstFinishedTask = await Task.WhenAny(downloadTasks);
downloadTasks.Remove(firstFinishedTask);
ossNew.Add(await firstFinishedTask);
}
return ossNew;
}
private async Task<SomeProduct> Convert(Mother ms, Child ss)
{
var ossNEW = new SomeProduct();
await update(ossNEW, ms);
return ossNEW;
}
private async Task update(SomeProduct oss, Mother ms)
{
await Task.Run(()=>
{
oss.copyOfTheLastOne = ms.internalField.First().theLastOne;
oss.valeFromAnUpdateFunction = "works";
});
}
To fully understand the problem, I would like to know why the UPDATE function does not return control to the main action and why RESULT on CONVERT function does not force to run program synchronously?
I would like to know why the UPDATE function does not return control to the main action and why RESULT on CONVERT function does not force to run program synchronously?
You're running into a common deadlock problem that I explain in full on my blog, due to the use of Result. Use await instead of Result and your problem goes away (in your case, since you have a collection, you'll want to await Task.WhenAll):
public async Task<SomeProduct[]> Get()
{
var collection = new List<Mother>() {
new Mother()
{
internalField = new List<Child>()
{
new Child()
{
theLastOne = "VAL"
},
new Child()
{
theLastOne = "VAL"
}
}
}
};
var oss =
from m in collection
from s in m.internalField
select Convert(m, s);
return Task.WhenAll(oss);
}
On a side note, you shouldn't use Task.Run in your implementations, particularly on ASP.NET. On ASP.NET, Task.Run completely removes all the benefits of async and adds overhead.

Making an asynchronous function synchronous for the Node.js REPL

I have a library that connects to a remote API:
class Client(access_token) {
void put(key, value, callback);
void get(key, callback);
}
I want to set up a Node.js REPL to make it easy to try things out:
var repl = require('repl');
var r = repl.start('> ');
r.context.client = new Client(...);
The problem is that an asynchronous API is not convenient for a REPL. I'd prefer a synchronous one that yields the result via the return value and signals an error with an exception. Something like:
class ReplClient(access_token) {
void put(key, value); // throws NetworkError
string get(key); // throws NetworkError
}
Is there a way to implement ReplClient using Client? I'd prefer to avoid any dependencies other than the standard Node.js packages.
You can synchronously wait for stuff with the magic of wait-for-stuff.
Based on your example specification:
const wait = require('wait-for-stuff')
class ReplClient {
constructor(access_token) {
this.client = new Client(access_token)
}
put(key, value) {
return checkErr(wait.for.promise(this.client.put(key, value)))
}
get(key) {
return checkErr(wait.for.promise(this.client.get(key)))
}
}
const checkErr = (maybeErr) => {
if (maybeErr instanceof Error) {
throw maybeErr
} else {
return maybeErr
}
}

Resources