How to avoid race conditions when assigning custom methods to WebSocket? - javascript

When I look at tutorials/documentation about WebSockets I find code like this:
var ws = new WebSocket("ws://localhost:8765/dlt");
ws.onopen = () => {
// do some very important stuff after connection has been established
console.log("onopen");
}
But what about race conditions here? Are there somehow avoided in JavaScript?
For example this code (which just assigns onopen after the connection has been opened) will fail:
var ws = new WebSocket("ws://localhost:8765/dlt");
setTimeout(() => {
ws.onopen = () => {
// do some very important stuff after connection has been established
console.log("onopen"); /// <== won't be called
}
}, 100);
Can I be sure that the assignment has been done before the connection get's established?
(I tried to extend WebSocket with a custom onopen() method but this doesn't seem to work)
class MyWebSocket extends WebSocket {
onopen() {
console.log("onopen()");
/// do some very important stuff after connection has been established
}
}

You should have a read about javascript's event loop: https://developer.mozilla.org/en-US/docs/Web/JavaScript/EventLoop#Event_loop
If you look at the section about Run-to-completion, you get this useful explanation:
Each message is processed completely before any other message is processed. This offers some nice properties when reasoning about your program, including the fact that whenever a function runs, it cannot be pre-empted and will run entirely before any other code runs (and can modify data the function manipulates). This differs from C, for instance, where if a function runs in a thread, it may be stopped at any point by the runtime system to run some other code in another thread.
So in your example, the assignment to ws.onopen must be completed before the websocket does anything asynchronous in nature. By putting your assignment inside setTimeout, you are moving it outside of the currently running context, and so it may not be executed before it is required by the websocket.

You should rest assured that the example is ok. The Javascript event loop will finish the current task before assuming any other tasks. This means that 1) the WebSocket cannot open the connection (async operation) before the onopen event, 2) the onopen event handler will be called during the following cycles.
Setting the timeout on the other hand will complicate matters, because the events will be called in some order after the current task. This means that that the WebSocket has chance to open the connection before the handler has been set.

Related

eventemitter.emit method doesn't return in node js

I have a problem using eventemitter.emit method.
Basically this is what I want to do. I have a long running process (CPU bounded) that generates output objects, and since this is CPU bounded process i run it as a separate process using fork().
class Producer extends EventEmitter {
constructor() {
this.on('MyEvent', this.produce);
}
produce(input) {
var output = longRunningProcess();
this.emit('MyEvent, output);
process.send(output);
}
}
var producer = new Producer();
producer.emit('MyEvent', 0); // To kick off the execution
And once each output is generated, I want to send it to the parent process. And also use it to emit an event to produce another object and so on.
Now, the problem is that the process.send(output) doesn't seem to be executed. I can see the outputs being printed in the console one after one. But the parent doesn't seem to be receiving anything from the child process. In my understanding, nodejs event loop shouldn't pick up a new task until it finishes the current one and the stack is empty, but this is not the case here.
So can you guys help me with this?
Edit: Parent process code
this.producer = ChildProcess.fork('.path/to/produer.js'silent: true });
this.producer.on('message', (data) => {
this.miningProcess.send({ type: "StopMining", body: 0 });
});
It looks to me like you may be starving the event loop (never giving it any cycles to processing incoming events) which can wreck the ability to process networking, even outbound networking. I'd suggest that you start the next iteration only after the process.send() has completed.
class Producer extends EventEmitter {
constructor() {
this.on('MyEvent', this.produce.bind(this));
}
produce(input) {
let output = longRunningProcess();
process.send(output, () => {
// When the send finishes, start the next iteration
// This should allow the node.js event queue to process things
this.emit('MyEvent, output);
});
}
}
var producer = new Producer();
producer.emit('MyEvent', 0); // To kick off the execution
Other comments of note:
You need this.produce.bind(this) on your event handler instead of just this.produce to make sure the right this value is set when that function is called.
Keep in mind that eventEmitter.emit() is synchronous. It does not allow the event queue to process events and eventEmitter events do not go through the event queue.
This code assumes that the process.send() callback is called asynchronously and gives the event loop enough chances to process any events that are waiting. It also makes sure the interprocess message is completely sent before you start the next CPU intensive iteration which will temporarily block the event queue processing again. This way, you are sure the whole communication is done before blocking the event queue again.
You probably could have made things work with an appropriately places setTimeout() to kick off the next iteration, but I think it's more reliable to make sure the interprocess messaging is done before kicking off the next iteration.
FYI, if you're not using the EventEmitter you derive from for anything other than is shown here, then it isn't really needed. You could just call methods on your object directly rather than using EventEmitter events.

Can I view the event loop of a browser to learn the order of JS scheduled for execution?

I've inherited a codebase where the order in which JS executes is not clear since there's a lot of setTimeout calls, globals, and broken Promise chains. Rather than manually trace every execution path I'd like to capture what JS gets scheduled for execution on the browser's message queue over a time period, or in response to an event.
I can see Event Listeners and trace from when one fires, but this is proving too slow in my case. A single click can sprawl out into several scheduled scripts that each mutate a shared state. This is why I am not considering tracing from event handlers and am instead looking for an overarching timeline for all JS in the application.
Given that JS scripts are scheduled for execution, how I can see the order in which JS gets queued?
I've started with something like this, but this doesn't give me a fully reliable timeline.
const {
setTimeout,
setInterval,
} = window;
window._jsq = [];
window._record = f => {
window._jsq.push([f, new Error().stack]);
};
window.setTimeout = (...a) => {
window._record(a[0]);
return setTimeout.apply(window, a);
};
window.setInterval = (...a) => {
window._record(a[0]);
return setInterval.apply(window, a);
};
I'll take a crack at my own question from the angle of the OP snippet. Corrections appreciated.
Assuming you cannot see the message queue (or at least the scripts queued), you can still see the code that is scheduling other JS and the code that is scheduled to run. So, tracking both independently is possible.
This is not all good news because you still have to do legwork to 1) adapt that tracking to the various ways JS can get scheduled, and 2) make sense of what you capture.
In the setTimeout case, something quick and dirty like this can at least provide a sense of a scheduling timeline and when things actually happened. That's just a matter of wrapping functions.
const { setTimeout } = window;
// For visibility in DevTools console
window._schedulers = [];
window._calls = [];
const wrap = f => {
const { stack } = new Error();
window._schedulers.push([stack, f]);
return (...a) => {
window._calls.push([stack, f, a]);
return f(...a);
};
};
window.setTimeout = (f, delay, ...a) => {
return setTimeout.apply(window, [wrap(f), delay].concat(a));
}
Still, that's just one case and says nothing about when to start/stop monitoring and the potential trigger points where traceability is a concern as Mosè Raguzzini mentioned. In the case of Promises, this answer calls out Bluebird's checking facilities.
It seems that until more native tools come out that visualize queued scripts and related info, you are stuck collecting and analyzing the data by hand.
There is no built-in automatic debugging tool for monitoring your browser event loop.
In order to monitor the browser's event loop you have to explicity monitor the event that are in your interested in and pass it to the (in this case Chrome's) DevTool:
monitorEvents(document.body, "click");
More info about monitoring events in Chrome Dev Tools
Note #1: You don't know how custom events are called. They may not dispatch an event into the DOM (e.g. some libraries implement their own event registration and handling systems) so there is no general way of knowing when event listeners are being called, even if you can track the dispatch of the event.
Some libraries also simulate event bubbling, but again, unless you know the type of event, you can't listen for it.
However, you could implement your own event management system and implement a function to listen for all events for which listeners are set or events dispatched using your system.
Ref: How can I monitor all custom events emitted in the browser?
Note #2: a modern JS approach to events (IE: React/Redux) involves dispatching ACTIONS instead of events. As actions are often logged for time-travel purpose, monitoring events in this case is unnecessary.

Why is my Web Worker terminated?

I'm trying to get a handle on web workers when I came across a very peculior behaviour. For some reason it's terminated after a few seconds, even though I have code in it that's running.
Here's my code;
Main JavaScript-file:
$(document).ready(function () {
var worker = new Worker("js/TestWorker.js");
worker.addEventListener('message', function (event) {
console.log(event.data);
});
worker.addEventListener('error', function (event) {
console.log(event);
});
});
Worker file:
(function () {
var updateCounter = 0;
var updater = function () {
updateCounter += 1;
console.log("Update counter: " + updateCounter);
postMessage("test");
setTimeout(updater, 10000);
};
updater();
})();
As stated, the worker just stops functioning after a few seconds, 10-20seconds or so.
But if I add this piece of code to my main JavaScript-file;
var check = function () {
var localWorker = worker;
// setTimeout(check, 1000);
};
// setTimeout(check, 1000);
The worker works as intended. The setTimeout-calls aren't needed either, hence why they're commented out. (Note that I can just aswell replace the assignment with worker.length or something similar and it will still work just fine.
Can someone explain this behaviour? Is the worker getting terminated and (erroneously) garbage-collected by the browser or is something else happening here that I'm missing?
Worth to note is that my browser (Chrome) isn't outputing any errors or warnings to the console either.
EDIT: The same behaviour is observed whether the code is executed inside an anonymous function or not.
EDIT2: If I place the worker variable in the global scope it does not get terminated prematurely. What might be the reason for this?
Some research shows that while web workers are supposed to function as you expect (i.e. won't be perceptibly garbage collected), there are some known issues in Chrome which mean you can't rely on that behaviour.
Of specific interest would be this very similar bug: https://bugs.chromium.org/p/chromium/issues/detail?id=572225 which in turn references a more underlying bug: https://bugs.chromium.org/p/chromium/issues/detail?id=572226
It seems to be due to an attempt to garbage collect workers which cannot possibly perform any future activities (in which case the garbage collection would be undetectable, as it's supposed to be), but a flaw in the logic for detecting this state means that any pending activities which aren't directly related to responding to an incoming message will be ignored.
Basically, while you should be able to assume web-workers behave like DOM nodes which can only be removed explicitly, in practice (for now) you need to make sure you always keep a reference to the worker accessible from somewhere, otherwise when the garbage collector kicks in it may kill the worker. This is only necessary if you're using setTimeout or similar in the worker; if it just responds to messages, you won't have a problem.
Maybe worker var must be global
var worker;
$(document).ready(function () {
worker = new Worker("js/TestWorker.js");
worker.addEventListener('message', function (event) {
console.log(event.data);
});
worker.addEventListener('error', function (event) {
console.log(event);
});
});
(function () {
...
})();
This is a anonymous function which will be called once after definition and after that the browser throws it away.
Your web worker is defined it that scope and that's why it's only working for a short period of time.

Callback NodeJS Javascript function from multithreaded C++ addon

I have a multithreaded C++ addon that does some background processing and I need to have it periodically callback to a Javascript function that I wrote in my NodeJS server.
I understand that this involves using uv_async_send(), since it needs to get executed in the main thread, but thus far I haven't been able to figure out how to do it.
Is there a simple example out there that I've missed?
Finally, this was not too difficult once I understood what the the uv_* functions do:
1) Expose a function in the addon to allow Node to set the Javascript cb that will be periodically called back to:
Callback* cbPeriodic; // keep cbPeriodic somewhere
NAN_METHOD(setPeriodicCb) {
cbPeriodic = new Callback(info[0].As<Function>());
//...
}
2) Init UV with an uv_async_t instance and a function that will be executed in the main thread by UV when our worker thread calls uv_async_send()
uv_async_t async; // keep this instance around for as long as we might need to do the periodic callback
uv_loop_t* loop = uv_default_loop();
uv_async_init(loop, &async, asyncmsg);
void asyncmsg(uv_async_t* handle) {
// Called by UV in main thread after our worker thread calls uv_async_send()
// I.e. it's safe to callback to the CB we defined in node!
Nan::HandleScope scope;
v8::Isolate* isolate = v8::Isolate::GetCurrent();
Local<Value> argv[] = { v8::String::NewFromUtf8(isolate, "Hello world") };
cbPeriodic->Call(1, argv);
}
3) Call uv_async_send from a worker thread, passing our async instance above, whenever we need to do the periodic callback
uv_async_send(&async);
4) Finally, when we no longer need to execute the callback ever again, clean things up:
uv_close((uv_handle_t*) &async, NULL);
Addendum:
Since I wrote this answer I've run into other issues and finally learned some lessons about libuv. For completeness, you should understand:
Aside from uv_async_send, all libuv functions may only be called from the the main loop thread! (I had seen it mentioned that the others were not thread-safe, but this is too weak of a statement.) Even, for example, uv_async_init and uv_close must be called from the main loop thread.
If your uv_async_t instance is dynamically allocated, note that you may not free up memory until uv_close makes its callback to let you know that it is safe to do so.
I.e.:
auto async = new uv_async_t();
...
uv_close((uv_handle_t*)async, [](uv_handle_t* handle) {
delete handle;
});

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.

Categories

Resources