1

------NOTE: SEE SOLUTION AT THE END OF THIS POST-------

I'm trying to make a simple websocket library, that allows to have one callback on connect, one callback on message received and automatic reconnection if it does no receive a message after 5 seconds.

<html>

<script>
function ws(url, cb, concb){
    this.timeout = setTimeout(this.timeoutCB, 5000);
    this.url = url
    this.cb = cb;
    this.concb = concb;
    this.startConnection()
    }

ws.prototype.startConnection = () => {
    this.conn = new WebSocket(this.url);
    this.conn.onopen = () => {
        this.concb()
        console.log('Connected ');
        }

    this.conn.onclose = function() {
        console.log('Connection closed ');
        }

    this.conn.onmessage = (e) => {
        console.log('Received From Server: ' + e.data);
        clearTimeout(this.timeout);
        text = e.data;
        this.cb(text);
        this.timeout = setTimeout(this.timeoutCB, 5000);
        }
    }

ws.prototype.send = function(e) {
    this.conn.send(e);
    }

ws.prototype.timeoutCB = () => {
    alert("PRESS OK TO RECONNECT");
    this.timeout = setTimeout(this.timeoutCB, 5000);
    this.startConnection();
    }
    
w = new ws("127.0.0.1:9000", null, null);
</script>

</html>

I created a startConnection method to be able to call it in both the constructor and the timeoutCB method. The problem is, when I call this.startConnection from the constructor, this is not the ws object but window. I don't know how I can create a method startConnection I can call from both the constructor and another method such as timeoutCB


------------------- SOLUTION ----------------------------------------------------

Please review Nick Parsons' comments. Here's the code that works for me after applying his suggestions:

function ws(url, cb, concb){
    this.url = url
    this.cb = cb;
    this.concb = concb;
    this.startConnection()
    }

ws.prototype.startConnection = function() {
    this.timeout = setTimeout(() => this.timeoutCB(), 5000);
    console.log(this)
    this.conn = new WebSocket(this.url);
    this.conn.onopen = () => {
        this.concb()
        console.log('Connected ');
        }

    this.conn.onclose = () => {
        console.log('Connection closed ');
        }

    this.conn.onmessage = (e) => {
        console.log('Received From Server: ' + e.data);
        clearTimeout(this.timeout);
        text = e.data;
        this.cb(text);
        this.timeout = setTimeout(() => this.timeoutCB(), 5000);
        }
    }

ws.prototype.send = function(e) {
    this.conn.send(e);
    }

ws.prototype.timeoutCB = function() {
    alert("PRESS OK TO RECONNECT");
    this.startConnection();
    }

javanoob
  • 163
  • 1
  • 1
  • 10
  • 1
    Calling `timeoutCB` with `setTimeout(this.timeoutCB, 5000);` will make `timeoutCB` get its `this` bound to the global object (ie: `window`) when called. You can either bind the `this` by using `setTimeout(this.timeoutCB.bind(this), 5000);`, or call the function yourself to keep the context: `setTimeout(() => this.timeoutCB(), 5000);` – Nick Parsons Feb 14 '21 at 07:39
  • I undertand the problem in setTimeout, however the first problem I found was in startConnection which I don't think it is the same as I'm not passing the reference to the function to anyone but I am directly calling the function. – javanoob Feb 14 '21 at 08:05
  • 1
    Oh yeah, you also need to change your arrow function `() => {}` to a regular `function() {}` so that it can get bound a `this` value (arrow function's use the `this` from the surrounding scope). So `ws.prototype.timeoutCB = function() {...}` – Nick Parsons Feb 14 '21 at 08:08
  • Isn't the surrounding scope the constructor of the object and hence `this` should be the object being constructed ? – javanoob Feb 14 '21 at 08:17
  • 1
    The surrounding scope is the scope outside of where the function is defined, not where it is called. The scope outside of where the function is defined is the global scope, and so the `this` is the default global object - `window`. If you `console.log(this)` above `ws.prototype.timeoutCB` you'll be able to see what `this` is in the surrounding scope – Nick Parsons Feb 14 '21 at 08:33
  • does that mean arrow functions should never be used when defining protoype methods ? – javanoob Feb 14 '21 at 08:40
  • 1
    If you need the `this` to refer to the object instance then yes. Otherwise, if you're not using `this` in the prototype method or need it to refer to the `this` from the surrounding scope then using arrow functions might be preferred. – Nick Parsons Feb 14 '21 at 08:48
  • I don't know if we are extending too much, but I think it is still relevant on how `this` works. I turned all arrow functions to trad. functions also in the functions defined inside `startConnection` (the ones with `this.conn.xxx` but now `this` turned out to be the webSocket object itself not the wrapping object. Should they be arrow functions, or should I pass `this` as `that` or `self` in this case? – javanoob Feb 14 '21 at 08:59
  • On a side note, my code is working much better after applying your suggestions :) – javanoob Feb 14 '21 at 09:04
  • Let us [continue this discussion in chat](https://chat.stackoverflow.com/rooms/228690/discussion-between-nick-parsons-and-javanoob). – Nick Parsons Feb 14 '21 at 09:30

0 Answers0