JavaScript same functions, different implementation decided on runtme - javascript

What is the best way to change JavaScript implementations at run time?
I have a web application which connects to the server by SignalR.
If there is any problem connecting to the server using SignalR at runtime, I want to change the services functions implementations to work with regular XHR.
I have one js file with the following functions to connect via SignalR:
function initializeConnection() {
// Initialize connection using SignalR
}
function sendEcho() {
// Sending echo message using signalR
}
And another js file with the same functions for connection via XHR:
function initializeConnection() {
// Initialize connection using XHR
}
function sendEcho() {
// Sending echo message using XHR
}
I know it is impossible to have them loaded at the same time.
I know I can use one file with a toggle within each function.
I thought maybe I can switch between these files by loading & unloading them at runtime. Is this possible? If so, is this the best way for such an issue?
What is the best way for supplying different implementations at runtime?

One way to do it, is to define both implementations as objects with same signatures and just set the namespace to a variable:
;var MyStuff = {
//SignalR
SignalR: {
initializeConnection: function(){console.log('SignalR.initializeConnection()')},
sendEcho: function(){console.log('SignalR.sendEcho()')}
},
//XHR
XHR: {
initializeConnection: function(){console.log('XHR.initializeConnection()')},
sendEcho: function(){console.log('XHR.sendEcho()')}
}
};
//Do whatever check you want to
var mNamespace = (1 === 2) ? MyStuff.SignalR : MyStuff.XHR;
//Call the instance
mNamespace.initializeConnection();
You can also keep them split in two files and add them both to MyStuff dynamicallly:
//File 1
;var MyStuff = (MyStuff === undefined) ? {} : MyStuff;
MyStuff.SignalR = {..};
//File 2
;var MyStuff = (MyStuff === undefined) ? {} : MyStuff;
MyStuff.XHR = {..};

One pattern that can help you is the "lazy function definition" or "self-defining function" pattern. It consists of (as its name points out) the redefinition of a function at runtime. It's useful when your function has to do some initial preparatory work and it needs to do it only once.
In your case, this "preparatory" work would be selecting the function that handles the client-server connection.
For instance:
var sendMessage = function() {
// Perform a check, or try a first message using your default connection flavour
// Depending on the result, redefine the function accordingly
sendMessage = sendMessageUsingWhatever;
};
//Use sendMessage anywhere you want, it'll use the proper protocol
This pattern was particularly handy when dealing with browsers and their peculiarities:
var addHandler = document.body.addEventListener ?
function(target, eventType, handler) {
target.addEventListener(eventType, handler, false);
} :
function(target, eventType, handler) {
target.attachEvent("on" + eventType, handler);
};
In this case, it is useful to determine which which way to attach event listeners depending on the availability (or not) of a particular method.
It has its drawbacks though. For instance, any properties you've previously added to the original function will be lost when it redefines itself.
Hope it helps or at least gives you some ideas.

Related

Updating global variables with NodeJS

I'm beginner to NodeJS, so I'm not entirely sure what the best method to achieve this would be. Basically I want to create a global variable with a string, for instance 'USD', that would get updated whenever my 'set currency' event is fired. I want it to remain that way until the event is called again.
I am using EventEmitter to fire off some events, in one of my files I have the following.
var event = require('./events');
if (msg.content.includes('!currency set currency')) {
split = msg.content.split(' ');
event.emit('setCurrency', split[3])
}
And then inside the events file I'm doing something like the following.
var exchangePref;
var event = new events.EventEmitter();
event.on('setExchange', (exchange) => {
exchangePref = exchange;
return exchangePref;
});
modules.exports = event;
I understand that re-writing the variable inside a callback isn't going to do what I need it to do, but I'm quite lost with how to achieve what I need it to do due to the modules.exports = event part at the bottom, the calling function simply never gets the data. I've played around with creating a constructor, but even still I couldn't get it to work.
Any suggestions/ideas would be greatly appreciated.
I wouldn't use event emitter for this. Instead create a module along the lines of:
var exchangePrefs = { currency: "JPY" };
module.exports = {
setCurrency : function(newVal){ exchangePrefs.currency = newVal; },
getCurrency : function(){ return exchangePrefs.currency; }
};
Then in your various other modules you just:
require('./mymodule').setCurrency('USD');
and somewhere else
var currency = require('./mymodule').getCurrency();
I'm sure it can be made prettier, but I think you get the point. For almost all intents and purposes modules work like singletons. There are some gotchas, but nothing you'll run into too often. (Singleton pattern in nodejs - is it needed?)
Personally I'd use some sort of data persistence in the exchangePref-module just for peace of mind. Like redis, or saving to a json-file.

node.js setInterval not working in custom module

I am developing a web application in node.js to collect data from devices on a network using snmp. This is my first real encounter with node.js and javascript. In the app each device will be manipulated through a module I named SnmpMonitor.js. This module will maintain basic device data as well as the snmp and database connection.
One of the features of the app is the ability to constantly monitor data from smart metering devices. To do this I created the following code to start and stop the monitoring of the device. It uses setInterval to constantly send a snmp get request to the device. Then the event listener picks it up and will add the collected data to a database. Right now the listener just prints to show it was successful.
var dataOIDs = ["1.3.6.1.2.1.1.1.0","1.3.6.1.2.1.1.2.0"];
var intervalDuration = 500;
var monitorIntervalID;
var dataCollectionEvent = "dataCollectionComplete";
var emitter = events.EventEmitter(); // Uses native Event Module
//...
function startMonitor(){
if(monitorIntervalID !== undefined){
console.log("Device monitor has already started");
} else {
monitorIntervalID = setInterval(getSnmp,intervalDuration,dataOIDs,dataCollectionEvent);
emitter.on(dataCollectionEvent,dataCallback);
}
}
function dataCallback(recievedData){
// receivedData is returned from getSnmp completion event
// TODO put data in database
console.log("Event happened");
}
function stopMonitor(){
if(monitorIntervalID !== undefined){
clearInterval(monitorIntervalID);
emitter.removeListener(dataCollectionEvent,dataCallback);
} else {
console.log("Must start collecting data before it can be stopped");
}
}
//...
I also have a test file, test.js, that requires the module, starts monitoring, waits 10 seconds, then stops it.
var test = require("./SnmpMonitor");
test.startMonitor();
setTimeout(test.stopMonitor,10000);
My problem is that the setInterval function in startMonitor() is not being run. I have tried placing console.log("test"); before, inside, and after it to test it. The inside test output never executes. The monitorIntervalID variable is also returned as undefined. I have tested setInterval(function(){ console.log("test"); },500); in my test.js file and it runs fine with no issues. I feel like this is a noobie mistake but I just can't seem to figure out why it won't execute.
Here is a link to the entire module: SnmpMonitor.js
I not sure exactly what was wrong but I got it to work by overhauling the whole class/module. I thought the way I had it was going to allow me to create new monitors objects but I was wrong. Instead I created two functions inside the monitor file that do the same thing. I changed the start function to the following.
SnmpMonitor.prototype.start = function() {
var snmpSession = new SNMP(this.deviceInfo.ipaddress,this.emitter);
var oids = this.deviceInfo.oids;
var emit = this.emitter;
var duration = this.intervalDuration;
this.intervalID = setInterval(function(){
snmpSession.get(dataCollectionEvent,emit,oids);
},duration);
};
The setInterval function seems to work best when the callback function is set inside an anonymous function, even though technically you can pass it directly. Using the this. notation I created some class/module/function variables (whatever its called in js) that are in scope of the whole class. For some reason the variables accessed through this. do not work so well when directly in a function or expression so I created temp variables for them. In my other version all the variables were global and js doesn't seem to like that.

add to WebSocket.onmessage() like how jQuery adds to events?

I'm writing a single page ws++ site, and I'd like to keep my code grouped first by "page" (I think I need a new word since it never posts back) then by section then by concept etc.
I'd like to split up WebSocket.onmessage across my code much in the same way that $('#someElement') can constantly have an event like click(function(){}) added to it.
Can this be done with WebSocket.onmessage(function(){})? If so, how?
As some jQuery programmers happily know, an event can be initially set then added to in multiple places across the js. That's my favorite thing about js, the "put it anywhere as long as it's in order" ability. This makes code organization so much easier for me at least.
With WebSockets, really, the action client side for me so far is with the WebSocket.onmessage() handler since WebSocket.send() can be used anywhere and really just ports js data to the server.
onmessage() now owns my page, as whatever's in it initiates most major actions such as fading out the login screen to the first content screen upon a "login successful" type message.
According to my limited understanding of js, the onmessage() handler must be set all in one place. It's a pain to keep scrolling back/tabbing to another file to make a change to it after I've changed the js around it, far, far, away.
How can I add to the WebSocket.onmessage() handler in multiple places across the js?
To answer your last question;
how can I add to onmessage handler in multiple places across the js?
You can define your own personal (global) event handler in which you accept arbitrary number of handler functions. Here's an example:
window.bind: function(name, func, context) {
if (typeof this.eventHandlers[name] == "undefined") {
this.eventHandlers[name] = [func];
this.eventContexts[name] = [context];
}
else {
var found = false;
for (var index in this.eventHandlers[name]) {
if (this.eventHandlers[name][index] == func && this.eventContexts[name][index] == context) {
found = true;
break;
}
}
if (!found) {
this.eventHandlers[name].push(func);
this.eventContexts[name].push(context);
}
}
}
window.trigger: function(name, args) {
if (typeof this.eventHandlers[name] != "undefined") {
for (var index in this.eventHandlers[name]) {
var obj = this.eventContexts[name][index];
this.eventHandlers[name][index].apply(obj, [args]);
}
}
}
// === Usage ===
//First you will bind an event handler some where in your code (it could be anywhere since `.bind` method is global).
window.bind("on new email", function(params) { ... });
//Then you need to trigger "on new email" in `onmessage` once appropriate events happen.
WebSocket.onmessage(function(data) {
//Work on data and trigger based on that
window.trigger("on new email", { subject: data.subject, email: data.email });
})
This code is a part of an open source project I worked on before. It gives events names and let you set context for your handler (for methods instead of functions). Then you can call trigger in your onmessage handler of your socket. I hope this is what you are looking for.
You can create a wrapper which will handle WS events on itself. See this example CoffeeScript:
class WebSocketConnection
constructor: (#url) ->
#ws = new WebSocket(#url)
#ws.onmessage = #onMessage
#callbacks = []
addCallback: (callback) ->
#callbacks.push callback
onMessage: (event) =>
for callback in #callbacks
callback.call #, event
# and now use it
conn = new WebSocketConnection(url)
conn.addCallback (event) =>
console.log event
You can do it with addEventListener :
socket.addEventListener('message', function (event) {
console.log('Message from server ', event.data);
});
I've constructed a CoffeeScript class to solve this problem. It's similar to #Valent's but a bit more full-featured, so I figured I'd share it. It provides "on", "off", and "clear" methods for web socket events and also provides forwarding functions for "send" and "close" so that you pretty much don't have to touch the socket directly. If you do need access to the actual WebSocket object, you can get there by superWebSocket.ws.
edit: I added a getConnection static method to produce url-dependent singletons. This way there's only one connection per url and if you attempt to create a 2nd, it just gives you the existing one. It also protects against anyone calling the constructor directly.
edit: I communicate across the socket in JSON. I added some code that will run JSON.stringify on any non-string passed into send and also will attempt to run JSON.parse on any message received via a handler.
superSockets = {}
class SuperWebSocket
#getConnection: (url)->
superSockets[url] ?= new SuperWebSocket url
superSockets[url]
constructor: (url)->
if arguments.callee.caller != SuperWebSocket.getConnection
throw new Error "Calling the SuperWebSocket constructor directly is not allowed. Use SuperWebSocket.getConnection(url)"
#ws = new WebSocket url
events = ['open', 'close', 'message', 'error']
#handlers = {}
events.forEach (event)=>
#handlers[event] = []
#ws["on#{event}"] = (message)=>
if message?
try
message = JSON.parse message.data
catch error
for handler in #handlers[event]
handler message
null
on: (event, handler)=>
#handlers[event] ?= []
#handlers[event].push handler
this
off: (event, handler)=>
handlerIndex = #handlers[event].indexOf handler
if handlerIndex != -1
#handlers[event].splice handlerIndex, 1
this
clear: (event)=>
#handlers[event] = []
this
send: (message)=>
if typeof(message) != 'string'
message = JSON.stringify message
#ws.send message
close: => #ws.close()

PhantomJS: Ensuring that the response object stays alive in server.listen(...)

I'm using server.listen(...) from PhantomJS. I realize that it is largely experimental and that it shouldn't be used in production. I'm using it for a simple screenshot-server that accepts generates screenshots for a URL; it's a toy project that I'm using to play around with PhantomJS. I've noticed an issue with long-running requests in particular, where the response object is unavailable. Here are the relevant snippets from my code:
var service = server.listen(8080, function (request, response) {
response.statusCode = 200;
if (loglevel === level.VERBOSE) {
log(request);
} else {
console.log("Incoming request with querystring:", request.url);
}
var params = parseQueryString(request.url);
if (params[screenshotOptions.ACTION] === action.SCREENSHOT) {
getScreenshot(params, function (screenshot) {
response.headers["success"] = screenshot.success; //<-- here is where I get the error that response.headers is unavailable. Execution pretty much stops at that point for that particular request.
response.headers["message"] = screenshot.message;
if (screenshot.success) {
response.write(screenshot.base64);
} else {
response.write("<html><body>There were errors!<br /><br />");
response.write(screenshot.message.replace(/\n/g, "<br />"));
response.write("</body></html>");
}
response.close();
});
} else {
response.write("<html><body><h1>Welcome to the screenshot server!</h1></body></html>")
response.close();
}
});
getScreenshot is an asynchronous method that uses the WebPage.open(...) function to open a webpage; this function is also asynchronous. So what seems to be happening is that when the callback that is passed in as an argument to getScreenshot is finally called, it appears that the response object has already been deleted. I basically end up with the following error from PhantomJS:
Error: cannot access member `headers' of deleted QObject
I believe this is because the request times out and so the connection is closed. The documentation mentions calling response.write("") at least once to ensure that the connection stays open. I tried calling response.write("") at the beginning of server.listen(...) and I even tried a pretty hacky solution where I used setInterval(...) to perform a response.write("") every 500 milliseconds (I even lowered it down to as little as 50). I also made sure to clear the interval once I was done. However, I still seem to get this issue.
Is this something that I'm just going to have to deal with until they make the webserver module more robust? Or is there a way around it?
I was able to figure this out. It appears that while loading certain pages with WebPage.open (for example http://fark.com and http://cnn.com) multiple onLoadFinished events are fired. This results in the callback in WebPage.open being called multiple times. So what happens is that when control comes back to the calling function, I've already closed the response and so the response object is no-longer valid. I fixed this by using creating a flag before the WebPage.open function is called. Inside the callback, I check the status of the flag to see if I've already encountered a previous onLoadFinished event. Once I am with whatever I have to do inside the WebPage.open callback, I update the flag to show that I've finished processing. This way spurious (at least in the context of my code) onLoadFinished events are no-longer serviced.
(Note that the following refers to PhantomJS 1.9.7 while the OP was likely referring to 1.6.1 or older.)
In the event that multiple onLoadFinished events are being fired, you can use page.open() instead of listening for onLoadFinished yourself. Using page.open() will wrap your handler in a private handler to ensure that your callback is only called once.
From the source:
definePageSignalHandler(page, handlers, "_onPageOpenFinished", "loadFinished");
page.open = function (url, arg1, arg2, arg3, arg4) {
var thisPage = this;
if (arguments.length === 1) {
this.openUrl(url, 'get', this.settings);
return;
}
else if (arguments.length === 2 && typeof arg1 === 'function') {
this._onPageOpenFinished = function() {
thisPage._onPageOpenFinished = null;
arg1.apply(thisPage, arguments);
}
this.openUrl(url, 'get', this.settings);
return;
}
// ... Truncated for brevity
This functionality is exactly the same as the other answer, exposed as part of the official API.

multithreading And Subscribe/Publish approach in javascript

I understand that there is no multithreading support in javascript. And i wanted some expert advice on the below scenario..
My requirement is to perform a AJAX call and upon successful completetion, i want to trigger set of events (to update the different parts of UI parallely)
I am planned to use Subscribe/Publish pattern, is it possible to subscribe multiple listners to the AJAX completion event.
If possible, i wanted to know how these listners notified on publish.. (parallely in muthithreaded way or one by one).
And suggest me the best way to achive this one.. I really appreciate your thoughts.
EDIT::
I know there are popular frameworks like JQuery supports this pattern. But am in a situation to develop this functionality from a scratch (we have our own framework).
I've a Request Pooler that might give you a good head-start here. [Since this answer was accepted I've retired the pooler in favor of a more complete "AJAX Helper" - the link has been updated.]
I'm not sure that'll do everything you want (although it sounds like it may be close). It's old, but it works:
Depressed Press DP_AJAX
It supports multiple simultaneous requests with timeout/retry, per-request handlers, has a very small footprint and can be combined with other code easily.
You create a pool (telling it how many simultaneous requests are allowed) and then toss requests at it. When they're done they call whatever handler you specified.
A small, complete example of it's use:
// The handler function
function AddUp(Num1, Num2, Num3) {
alert(Num1 + Num2 + Num3);
};
// Instantiate the Pool
myRequestPool = new DP_RequestPool(4);
// Start the Interval
myRequestPool.startInterval(100);
// Create the Request
myRequest = new DP_Request(
"GET",
"http://www.mysite.com/Add.htm",
{"FirstNum" : 5, "SecondNum" : 10},
AddUp,
[7,13]);
// Add the request to the queue
myRequestPool.addRequest(myRequest);
It's open source - feel free to chop/fold/spindle or mutilate it to your hearts content.
Jim Davis
This article describes what you're trying to accomplish pretty closely. Essentially you just have a JavaScript file that holds an array of handlers/subscribers. Each subscriber registers itself with the publisher (i.e. gets added to the handlers array). Then in the onClose handler of your Ajax call, you'd call a function that iterates over the subscribers and notifies them each by name:
this.handlers = [];
...
for(var i = 0; i < this.handlers.length; i++)
{
this.handlers[i].eventHandler.call(this, eventArgs);
}
...
Could this serve as a ligthweight message passing framework?
function MyConstructor() {
this.MessageQueues = {};
this.PostMessage = function (Subject) {
var Queue = this.MessageQueues[Subject];
if (Queue) return function() {
var i = Queue.length - 1;
do Queue[i]();
while (i--);
}
}
this.Listen = function (Subject, Listener) {
var Queue = this.MessageQueues[Subject] || [];
(this.MessageQueues[Subject] = Queue).push(Listener);
}
}
then you could do:
var myInstance = new MyConstructor();
myInstance.Listen("some message", callback());
myInstance.Listen("some other message", anotherCallback());
myInstance.Listen("some message", yesAnotherCallback());
and later:
myInstance.PostMessage("some message");
would dispatch the queues
This is possible and you should really use a library for that to avoid all the browser incompatibilities. For example jQuery provides Ajax functionality that lets you execute code on completion of the Ajax call. See here for documentation.
if you want to fire functions in parralell use following:
this.handlers = [];
...
for(var i = 0; i < this.handlers.length; i++)
{
setTimeout(function(){this.handlers[i].eventHandler.call(this, eventArgs);},0);
}
...

Categories

Resources