Reputation: 16831
There's a particular JavaScript pattern that has always bothered me, and I never really figured out the proper way to solve it. Instead I usually just ignore it because 99% of JavaScript interpreters support function hoisting so no run-time errors arise.
Consider the following:
function onOpen()
{
console.log("Connected!");
ws.removeEventListener("open", onOpen);
ws.removeEventListener("error", onError);
}
function onError()
{
console.log("Failed to connect!");
ws.removeEventListener("message", onMessage);
ws.removeEventListener("error", onError);
}
var ws = new WebSocket("...");
ws.addEventListener("open", onOpen);
ws.addEventListener("error", onError);
In this code, within the onOpen
function, I'm referencing onError
before onError
has been defined lower in the code. This isn't actually a problem, because the onOpen
method won't run until after onError
has been defined, but it's still bad practice and trips up ESLint's no-use-before-define rule
In a more general sense, this is an error that will arise whenever two functions exist that each need to reference each other:
function a(x) {
return x === 1 ? b(x) : 2;
}
function b(x) {
return x === 2 ? a(x) : 1;
}
Is there a design pattern for eliminating this circular dependency? In my simple generic example the easy solution is "only have one function":
function a(x) {
return x === 1 ? 1 : 2;
}
However when binding event listeners this doesn't always seem possible.
Upvotes: 1
Views: 971
Reputation: 664970
It's still bad practice and trips up ESLint's no-use-before-define rule
No, it's not a bad practice, it's very much a necessity. It is the preferred pattern when you have a circular functional dependency. Configure your ESLint accordingly (with { "functions": false }
).
Is there a design pattern for eliminating this circular dependency?
Not really. There are several workarounds however, such as declaring the function up-front with a var
(which is enough to make ESLint happy). Alternatively, you could pass a reference to the function around via parameters:
function _a(x, f) {
return x === 1 ? f(x) : 2;
}
function b(x) {
return x === 2 ? _a(x, b) : 1;
}
function a(x) {
return _a(x, b);
}
Other crazy hacks with closures, following up on the Y combinator idea, are imaginable. This is not really suitable for your event listener scenario though.
Upvotes: 3
Reputation: 1041
I don't think this is a real problem, because usually everything works fine. Also you worry about the fact that you mention onError
before it is declared, but you don't care about mentioning ws
. But if yous still need to solve this, I hope this approach will do the work.
var eventListenerListOnOpen = [];
var eventListListenerOnError = [];
var eventListListenerOnMessage = [];
var ws;
function removeListedEventListeners(object, eventName, eventListenerList) {
eventListenerList.forEach(function(listener) {
object.removeEventListener(eventName, listener);
});
}
function onOpen() {
removeListedEventListeners(ws, "open", eventListenerListOnOpen);
removeListedEventListeners(ws, "error", eventListenerListOnError);
}
function onError() {
removeListedEventListeners(ws, "message", eventListenerListOnMessage);
removeListedEventListeners(ws, "error", eventListenerListOnError);
}
ws = new WebSocket("...");
ws.addEventListener("open", onOpen);
eventListenerListOnOpen.push(onOpen);
ws.addEventListener("error", onError);
eventListenerListOnError.push(onError);
Upvotes: 2