'Watch.js only unwatching in certain conditions?
I am developing an express-ws based app in Node.js, and I am using a Watch.js watcher within an app.ws route. When the WebSocket is opened, a watch is made on a global object, using a callback function defined within the app.ws statement. Then inside ws.on('close', ... function, I call unwatch with the same object and callback function. (See code snippet A)
This works for a simple JS object with simple boolean entries (See stat_A in snippet A). However, I will soon have multiple objects holding different statuses, and I would like to put them all in a new JS object. So that is an object with a bunch of objects inside.
Snippet B is an example that should be equivalent to Snippet A. However, it seems that after the WebSocket is closed, the unwatch somehow does not take, and when stat['A'] is updated, the program tries to send it over the closed WebSocket and crashes.
I am stumped. What is causing the unwatch to fail?
EDIT: I have gotten snippet B working by changing watch(stat, 'A', send_stat) to watch(stat['A'], send_stat), and also changing the unwatch in the same way. See snippet C. Now, I am unable to unwatch the main stat list, See snippet D.
Code snippet A (stat_A is updated elsewhere):
const express = require('express');
const app = express();
const expressWs = require('express-ws')(app);
const watchjs = require('watchjs');
const port = 1338;
var stat_A = {'1': true, '2': false, '3' : false};
app.ws(
'/stat_A',
(ws, req) => {
function send_stat() {
ws.send(JSON.stringify(stat_A));
}
send_stat();
// Setup watchers
watchjs.watch(stat_A, send_stat);
ws.on(
'close',
() => {
// Stop watchers
watchjs.unwatch(stat_A, send_stat);
}
);
}
);
app.listen(
port,
() => {
console.log(`Listening (HTTP) on port ${port}!`);
}
);
Code snippet B (stat['A'] is updated elsewhere):
const express = require('express');
const app = express();
const expressWs = require('express-ws')(app);
const watchjs = require('watchjs');
const port = 1338;
var stat = {
'A': {'1': true, '2': false, '3' : false}
};
app.ws(
'/stat_A',
(ws, req) => {
function send_stat() {
ws.send(JSON.stringify(stat['A']));
}
send_stat();
// Setup watchers
watchjs.watch(stat, 'A', send_stat);
ws.on(
'close',
() => {
// Stop watchers
watchjs.unwatch(stat, 'A', send_stat);
}
);
}
);
app.listen(
port,
() => {
console.log(`Listening (HTTP) on port ${port}!`);
}
);
Code snippet C (B fixed):
const express = require('express');
const app = express();
const expressWs = require('express-ws')(app);
const watchjs = require('watchjs');
const port = 1338;
var stat = {
'A': {'1': true, '2': false, '3' : false}
};
app.ws(
'/stat_A',
(ws, req) => {
function send_stat() {
ws.send(JSON.stringify(stat['A']));
}
send_stat();
// Setup watchers
watchjs.watch(stat['A'], send_stat);
ws.on(
'close',
() => {
// Stop watchers
watchjs.unwatch(stat['A'], send_stat);
}
);
}
);
app.listen(
port,
() => {
console.log(`Listening (HTTP) on port ${port}!`);
}
);
Code snippet D (Same issue as B, fix in C not applicable):
const express = require('express');
const app = express();
const expressWs = require('express-ws')(app);
const watchjs = require('watchjs');
const port = 1338;
var stat = {
'A': {'1': true, '2': false, '3' : false}
};
app.ws(
'/stat',
(ws, req) => {
function send_stat() {
ws.send(JSON.stringify(stat));
}
send_stat();
// Setup watchers
watchjs.watch(stat, send_stat);
ws.on(
'close',
() => {
// Stop watchers
watchjs.unwatch(stat, send_stat);
}
);
}
);
app.listen(
port,
() => {
console.log(`Listening (HTTP) on port ${port}!`);
}
);
Solution 1:[1]
So the final answer to this is as below.
const express = require('express');
const app = express();
const expressWs = require('express-ws')(app);
const watchjs = require('watchjs');
const port = 1338;
var stat = {
'A': {'1': true, '2': false, '3' : false}
};
app.ws(
'/stat',
(ws, req) => {
function send_stat() {
ws.send(JSON.stringify(stat));
}
send_stat();
// Setup watchers
Object.keys(stat).forEach((key) => {
watchjs.watch(stat[key], send_stat);
})
ws.on(
'close',
() => {
// Stop watchers
Object.keys(stat).forEach((key) => {
watchjs.unwatch(stat[key], send_stat);
})
}
);
}
);
app.ws(
'/stat/A',
(ws, req) => {
function send_stat() {
ws.send(JSON.stringify(stat['A']));
}
send_stat();
// Setup watcher
watchjs.watch(stat['A'], send_stat);
ws.on(
'close',
() => {
// Stop watcher
watchjs.unwatch(stat['A'], send_stat);
}
);
}
);
app.listen(
port,
() => {
console.log(`Listening (HTTP) on port ${port}!`);
}
);
Solution 2:[2]
That can be made with 2 options, one with a setter/getter, one with a getter and a setInterval
// option with a setter/getter
function flagWatch(callback, cval = null)
{
this.callback = callback;
this.get = () => cval;
this.set = function(ncval) {
if(cval != ncval)
{
let oldval = cval;
cval = ncval;
if(typeof this.callback == 'function')
this.callback(ncval, oldval);
}
};
return this;
};
var hr = new flagWatch((v, o) => {
console.log('new value ['+v+'], old value ['+o+']');
});
setInterval(function() {
hr.set(!hr.get());
}, 2000);
// option with a getter and a setInterval
function watchValue(getter, callback, interval = 1)
{
if(typeof getter != 'function' || typeof callback != 'function')
throw new Error('Getter or Callback is not a function');
var cval = getter();
setInterval(function() {
var ncval = getter();
if(cval != ncval)
{
let oldval = cval;
cval = ncval;
callback(ncval, oldval);
}
}, interval);
}
var t = true;
watchValue(() => t, (v, o) => {
console.log('changed to ['+v+'] (was ['+o+'])');
});
setInterval(function() {
t = !t;
}, 2000);
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
| Solution | Source |
|---|---|
| Solution 1 | |
| Solution 2 |
