How to dispatch custom messages across Javascript classes in an efficient way? - javascript

I have built loosely coupled components for a javascript client application where these components send and receive messages via a manager class:
a component detects an event => send to manager
manager receives an event => send to everyone
any component got message => interested in message? yes: process, no: discard.
This works as intended, but not efficient. Here is the implementation of manager:
in Livescript:
class mgr
~>
#actor-list = []
register: (actor) ->
#actor-list ++= [actor]
dispatch: (msg) ->
for actor in #actor-list
actor.inbox-put msg
in Javascript:
var mgr;
mgr = (function(){
mgr.displayName = 'mgr';
var prototype = mgr.prototype, constructor = mgr;
function mgr(){
var this$ = this instanceof ctor$ ? this : new ctor$;
this$.actorList = [];
return this$;
} function ctor$(){} ctor$.prototype = prototype;
prototype.register = function(actor){
return this.actorList = this.actorList.concat([actor]);
};
prototype.dispatch = function(msg){
var i$, ref$, len$, actor, results$ = [];
for (i$ = 0, len$ = (ref$ = this.actorList).length; i$ < len$; ++i$) {
actor = ref$[i$];
results$.push(actor.inboxPut(msg));
}
return results$;
};
return mgr;
}());
I have searched the net for "event dispatching in Javascript" and there is a couple of projects used for this purpose, but some of them seems dead:
https://github.com/mrdoob/eventdispatcher.js/
Some of them uses same for loop under the hood:
https://github.com/krasimir/EventBus
and some of them requires binding on an html node:
https://developer.mozilla.org/en-US/docs/Web/Guide/Events/Creating_and_triggering_events
What I need is pure javascript communication between Javascript classes. Is there any efficient/native way to achieve this?

Related

Websockets in Sapper

I have a readable store in Svelte that looks like this:
const state = {};
export const channels = readable(state, set => {
let st = state;
let socket = new WebSocket("ws://127.0.0.1:5999");
socket.onmessage = function (event) {
var datastr = event.data.split(':');
st[datastr[0]].value = datastr[1];
st[datastr[0]].timestamp = Date.now();
set(st)
};
return () => {
socket.close()
}
});
When I import it to my Svelte App works. But if I put that App.svelte as my index.svelte running on Sapper, it doesnt work at first. It says error 500 websocket is not defined. Once I reload the page in the browser start to work...
I have try to parse a function that creates the store instead:
export const getChannel = () => {
// here my store
return {...store}
}
and then creating the store inside a onMount() like this:
onMount( ()=> {
const channel = getChannel();
});
But doesnt seem to do the trick... What do I miss?
Note: If a just replace the store by a simple writable, and create the websocket onMount(), it works without any problem. I just only wanted to put all the communication inside the store as a readable...
In Sapper, code in components (or imported into components) is executed in Node during server-side rendering unless it's put inside onMount (which doesn't run on the server, because there's no 'mounting' happening) or an if (process.browser) {...} block, or something equivalent.
That includes things like references to $channels causing channels.subscribe(...) to be called during initialisation.
Since there's no WebSocket global in Node, creating that subscription will fail. The simplest workaround is probably a simple feature check:
const state = {};
export const channels = readable(state, (set) => {
if (typeof WebSocket === 'undefined') return;
let st = state;
let socket = new WebSocket("ws://127.0.0.1:5999");
socket.onmessage = function (event) {
var datastr = event.data.split(":");
st[datastr[0]].value = datastr[1];
st[datastr[0]].timestamp = Date.now();
set(st);
};
return () => {
socket.close();
};
});

JavaScript send out an alert to prompt and edit array?

//global variable
var memArray =[];
//object
function member(id, password){
this.id = id;
this.pwd = password
}
var memObj1=new member("m001","123");
memArray.push(memObj1);
How do I send out an alert to prompt and edit each object that is push to memArray?
if you want to customize it try to use your own modals instead of window.prompt and just display values with editable text fields, on submit capture those values and change them in array respectively.
var memArray = [];
//object
function member(id, password) {
this.id = id;
this.pwd = password
}
var memObj1 = new member("m001", "123");
var memObj2 = new member("m002", "123");
var memObj3 = new member("m031", "123");
memArray.push(memObj1);
memArray.push(memObj2);
memArray.push(memObj3);
memArray.forEach((val, ind) => {
memArray[ind] = JSON.parse(window.prompt("want to edit values?", JSON.stringify(memArray[ind])));
});
console.log(memArray)
Pavan's answer is good, but to make this testable in automated tests:
// I would name these fields in your API
// by making the constructor take an object.
// Capitalise the name to signal that it can be newed
function Member({id, password}) {
this.id = id;
this.pwd = password
}
// Name the array for what it is
const members = [
new Member({id: "m001", password: "123"}),
new Member({id: "m002", password: "123"}),
new Member({id: "m031", password: "123"})
]
const editInBrowserFn = member => JSON.parse(window.prompt("want to edit values?", JSON.stringify(member)));
const updateMembers = editFn => array => array.map(editFn)
// To get an update
const updatedMembers = updateMembers(editInBrowserFn)(members)
console.log(updatedMembers)
// You can now test it by making an testing editFn that doesn't need user interaction
const testEditFn = m => new Member({id: m.id, password: 'test'})
const testMembers = updateMembers(testEditFn)(members)
console.log(testMembers)
See this article for an in-depth explanation of this approach.
To do it this way, you will need to take it out of the global scope. That is a good discipline to develop. As a first step you could make an object in global scope that holds the latest member list:
const Members = (() => {
let _members = []
return {
setMembers: members => _members = [...members],
getMembers: () => [..._members]
}
})()
Now the way to update the members is like this:
const updateFn = updateMembers(editInBrowser)
function updatePasswords() {
const members = Members.getMembers()
Members.setMembers(updateFn(members))
}
Nothing can accidentally delete or mutate the members array now, so that bug surface area is eliminated.
This is how React setState is designed. It's inspired by functional programming ideas and immutability.
You probably want to be able to update just one member, so:
const Members = (() => {
let _members = []
return {
setMembers: members => _members = [...members],
getMembers: () => [..._members],
updateMember: updated =>
this.members = _members.map(m =>
m.id === updated.id ? updated : m)
}
})()
Now all your array mutation is in one single place, and you only have to make it bug-free there. Otherwise, your global state is exposed and you have to fix bugs everywhere related to it. Without this, all your calling functions are responsibly for correctly managing the global state of the application. Complex.
Crystallise the complexity in one place.
I wrote an article and a complete implementation of the store (in 40 lines of code) here.
As far as I concern, alerts are just models on the browser to provide informative feedback to a particular user on his actions. Therefore, I think it is required to use either a dialog model or a form to edit the objects in the memArray.

BreezeJs with dedicated web worker

I am trying to initialize a Breeze manager inside a 'Web Worker'.
RequireJs, knockout, q, breeze are being imported inside the worker.
After a call to:EntityQuery.from('name').using(manager).execute(),
the following error appears:
Uncaught Error: Q is undefined. Are you missing Q.js? See https://github.com/kriskowal/q.
A live preview is uploaded here http://plnkr.co/edit/meXjKa?p=preview
(plunk supports downloading for easier debug).
EDIT -- relevant code
Worker.js
importScripts('knockout.js', 'q.js', 'breeze.js', 'require.js');
define('jquery', function () { return jQuery; });
define('knockout', ko);
define('q', Q); //Just trying to assign q since breeze requests Q as q
require(function () {
var self = this;
this.q = this.Q; //Just trying to assign q since breeze requests Q as q
breeze.NamingConvention.camelCase.setAsDefault();
var manager = new breeze.EntityManager("breeze/Breeze");
var EntityQuery = breeze.EntityQuery;
// Q or q here is defined (TESTED)
var test = function (name) {
return EntityQuery.from(name)
.using(manager).execute() // <-- Here q/Q breaks (I think on execute)
};
var primeData = function () {
return test('Languages')
.then(test('Lala'))
.then(test('Lala2'))
};
primeData();
setTimeout(function () { postMessage("TestMan"); }, 500);
});
Worker will be initialized on main page as:
var myWorker = new Worker("worker.js");
Ok here it goes:
Create a new requireJs and edit the
isBrowser = !!(typeof window !== 'undefined' && typeof navigator !== 'undefined' && window.document)
to
isBrowser = false
Create a new Jquery so it uses nothing related to window and generally anything that a WebWorker cannot access. Unfortunatelly i can't remember where i got this Custom JQueryJs but i have uploaded it here "https://dl.dropboxusercontent.com/u/48132252/jqueydemo.js".
Please if you find the author or the original change link and give credit.
My workerJs file looks like:
importScripts('Scripts/test.js', 'Scripts/jqueydemo.js', 'Scripts/q.js', 'Scripts/breeze.debug.js', 'Scripts/require2.js');
define('jquery', function () { return jQuery; });
require(
{
baseUrl: "..",
},
function () {
var manager = new breeze.EntityManager("breeze/Breeze");
var EntityQuery = breeze.EntityQuery;
var primeData = function () {
return EntityQuery.from(name)
.using(manager).execute() // Get my Data
.then(function (data) {
console.log("fetced!\n" + ((new Date()).getTime()));
var exportData = manager.exportEntities(); // Export my constructed entities
console.log("created!\n" + ((new Date()).getTime()));
var lala = JSON.stringify(exportData)
postMessage(lala); // Send them as a string to the main thread
})
};
primeData();
});
Finally on my mainJs i have something like:
this.testWorker = function () {
var myWorker = new Worker("worker.js"); // Init Worker
myWorker.onmessage = function (oEvent) { // On worker job finished
toastr.success('Worker finished and returned');
var lala = JSON.parse(oEvent.data); // Reverse string to JSON
manager.importEntities(lala); // Import the pre-Constructed Entities to breezeManager
toastr.success('Import done');
myWorker.terminate();
};
};
So we have managed to use breeze on a WebWorker enviroment to fetch and create all of our entities, pass our exported entities to our main breeze manager on the main thread(import).
I have tested this with 9 tables fully related to each other and about 4MB of raw data.
PROFIT: UI stays fully responsive all the time.
No more long execution script, application not responding or out of memory errors) at least for chrome
*As it makes sense breeze import entities is way more faster than the creation a full 4MB raw data plus the association process following for these entities.
By having all the heavy work done on the back, and only use import entities on the front, breeze allows you to handle large datasets 'like a breeze'.

Javascript: inheriting encapsulated variables

Edit: Removed higher-level ideas, included problem-specific and less-transferable code.
I implemented my DAL using DAO's. My application hooks in to various databases (mostly for legacy reasons). In order to facilitate efficient and intelligent usage of connections, I use a ConnectionBroker singleton to manage the various connections that may (or may not be) open. This ConnectionBroker is then injected into the DAO's where they can request control of a particular connection object, request new connections, ect.
From an inheritence POV, I'd like something like:
AbstractDbConnection
|-- MongoDbConnection
|-- MsSqlConnection
|-- CouchDbConnection
|-- ...
Where AbstractDbConnection defines an interface, and implements some shared event-based logic.
var EventEmitter = require('events').EventEmitter;
module.exports = function AbstractDbConnection(host, port, database, login, ...) {
// private
var state = StatesEnum.Closed; // StatesEnum = {Open: 0, Closed: 1, ..}; Object.freeze(StatesEnum);
// api that must be overwritten
this.connect = function connect() {throw new ...}
this.disconnect = function disconnect() {throw new ...}
... <more>
this.getState = function() { return state; }
}
AbstractDbConnection.prototype.__proto__ = EventEmitter.prototype;
And then I implement the interface using driver-specific code:
var mssqldriver = require('mssqldriver'), //fictitious driver
AbstractDbConnection = require(__dirname + '/blah/AbstractDbConnection');
module.exports = function MsSqlConnection(host, port, database, login, ...) {
var me = this;
// implement using driver
this.connect = function connect() {...}
this.disconnect = function disconnect() {...}
... <more>
driverSpecificConnection.on('driverSpecificOpenEvent', function() {
me.emit('open'); // relay driver-specific events into common events
state = StatesEnum.Open; // how ??
}
...
}
MsSqlConnection.prototype.__proto__ = new AbstractDbConnection();
But clearly I want to protect the state property from changing inadvertently.
Just listen for the open event in the "abstract" constructor!
var EventEmitter = require('events').EventEmitter;
module.exports = AbstractDbConnection;
var StatesEnum = module.exports.StatesEnum = Object.freeze({
Open: 0, Closed: 1, …
});
function AbstractDbConnection(host, port, database, login, …) {
// private
var state = StatesEnum.Closed;
EventEmitter.call(this);
this.getState = function() { return state; }
this.on('open', function(e) {
state = StatesEnum.Open;
});
}
AbstractDbConnection.prototype = Object.create(EventEmitter.prototype);
// api that must be overwritten
AbstractDbConnection.prototype.connect = function connect() {throw new …};
AbstractDbConnection.prototype.disconnect = function disconnect() {throw new …};
var Mssqldriver = require('mssqldriver'), //fictitious driver
AbstractDbConnection = require(__dirname + '/blah/AbstractDbConnection');
module.exports = MsSqlConnection;
function MsSqlConnection(host, port, database, login, …) {
AbstractDbConnection.call(this);
this.driver = new Mssqldriver(…);
this.driver.on('driverSpecificOpenEvent', this.emit.bind(this, 'open'));
…
}
MsSqlConnection.prototype = Object.create(AbstractDbConnection.prototype);
MsSqlConnection.prototype.connect = function connect() {…};
MsSqlConnection.prototype.disconnect = function disconnect() {…};
You can use the module pattern to do this.
var transport_module = function() {
var mileage = 0; // private
return {
transport : function(distance) {
mileage += distance;
}
};
}
//use it
var car = transport_module(),
boat = transport_module(),
motorcycle = transport_module();
car.transport(10);
boat.transport(5);
motorcycle.transport(20);
The variable mileage is not visible to any other javascript code. Like a private java/C++ class variable. However, I would think about whether you need this kind of protection. I use modules a lot but not for small objects like class instances in java/C++.

Javascript pattern for maintaining state and creating nested objects that provide additional functionality

I am writing a Javascript API library that provides consumers with an interface that enables them to interact with our backend web services. It is envisioned that a consumer will be writing a javascript client web application that draws heavily on the API provided for by the library.
I have come up with this "pattern" for maintaining state and making functionality "available" as certain criteria are met (for example, an authenticated user is logged in client-side).
Is this an appropriate way to achieve that end? Or am I unwittingly breaking some convention or best practice that will bite me later on?
// file: clientApi.js (library)
ClientObject = function () {
this.objectname = "a client class";
}
ClientObject.prototype.loginUser = function(name) {
this.loggedin = true;
if (typeof this.User === 'undefined') {
this.User = new ClientObject.User(name);
}
}
ClientObject.User = function (name) {
this.username = name;
}
ClientObject.User.prototype.getProfile = function() {
return 'user profile';
}
// file: app.js (consuming application)
var testClient = new ClientObject();
console.log('testClient.User = ' + (typeof testClient.User)); // should not exist
testClient.loginUser('Bob'); // should login 'bob'
console.log('testClient.User = ' + (typeof testClient.User)); // should exist
console.log(testClient.User.username); // bob
testClient.loginUser('Tom'); // should not do anything
console.log(testClient.User.username); // bob still
console.log(testClient.User.getProfile()); // tada, new functionality available
My question: is this approach valid? Is there a pattern that I'm touching on that might offer a better explanation or method of achieving my end goal?
I asked a similar question here with a bunch of other ones, unfortunately the above code was somewhat lost in the noise: Javascript: creation of object from an already instantiated object versus the prototype
Your API should have some secrets. That's why do not make all your functions public. Let's analyze some parts of your code:
testClient.loginUser('Tom'); // should not do anything
But your implementation allows client to do next:
testClient.User = new ClientObject.User(name);
Now user will be changed to "Tom".
Let's change your clientApi.js code, using revealing prototype pattern:
ClientObject = function () {
this.objectname = "a client class";
this.username;
this.User;
this.loggedin;
}
ClientObject.prototype = function() {
var loginUser = function(name) {
this.loggedin = true;
if (typeof this.User === 'undefined') {
this.User = new User(name);
}
};
var User = function (name) {
this.username = name;
};
User.prototype.getProfile = function() {
return 'user profile';
};
return {
loginUser : loginUser
}
}()
Now client cannot change logged in User like in first version of the library. You can use some variations, but that's the idea.

Categories

Resources