1

I have websocket service that works great when page is loaded. However, if connection is lost, and the service is trying to reconnect I am getting an error: "Uncaught ReferenceError: Service is not defined". Once I manually refresh page, service is working again. How can I reconnect without page refreshing? The app must reestablish that connection without any user involvement. This is my first angular app, so I am still in the process of learning the framework. Thank you.

angular.module('proApp').factory('webSocketService',
    ['$q', '$rootScope', function($q, $rootScope) {

            var timeout = 2000;
            var clearTimer = -1;
            var port = '8081';
            var server = '127.0.0.1';
            var socket;
            var host;
            var Service = {};

            function getSocketState() {
                return (socket != null) ? socket.readyState : 0;
            }

            function onMessage(e) {
                //console.log(e.data);
                Service.message = JSON.parse(e.data);

                $rootScope.$apply(function() {
                    Service.send();
                });
            }
            //allows data to be used in controller
            Service.send = function() {
                $rootScope.$broadcast('broadcast');
            };
            function onError() {
                clearInterval(clearTimer);
                socket.onclose = function() {
                };
                clearTimer = setInterval("Service.getData()", timeout);
            }

            function onClose() {
                clearInterval(clearTimer);
                clearTimer = setInterval("Service.getData()", timeout);

            }

            function onOpen() {
                clearInterval(clearTimer);
                console.log("open" + getSocketState());
            }

            Service.getData = function() {

                if ("WebSocket" in window) {
                    if (getSocketState() === 1) {
                        socket.onopen = onOpen;
                        clearInterval(clearTimer);
                        console.log(getSocketState());
                    } else {
                        try {
                            host = "ws://" + server + ":" + port + '';
                            socket = new WebSocket(host);
                            socket.onopen = onOpen;
                            socket.onmessage = function(e) {
                                onMessage(e);
                            };
                            socket.onerror = onError;
                            socket.onclose = onClose;
                        } catch (exeption) {
                            console.log(exeption);
                        }
                    }
                }
            };
            // Public API here
            return Service;

        }]);
kaplievabell
  • 759
  • 1
  • 11
  • 27

2 Answers2

0

You have to change the use of setInterval to be like this:

clearTimer = setInterval(function () {
  Service.getData();
}, timeout);

or just this:

clearTimer = setInterval(Service.getData, timeout);
runTarm
  • 11,537
  • 1
  • 37
  • 37
0

This is the code I use, it can:

  • reconnect if connection is lost.
  • queue items while disconnected and send them on re-connection.
  • regular subscribing using the "listen" method.
  • "listenOnce" for an event once with a promise, after that the subscription is removed. Ideal for request/response using a correlationId
  • $rootScope.websocketAvailable indicates when the connection is available.
  • $rootScope.queuedMessages indicates when there are pending messages to be sent.

It is still part of a project in development, but I guess you can get the idea:

.service('$connection', ["$q", "$timeout", "websocketUrl", "$rootScope", function ($q, $timeout, websocketUrl, $rootScope) {
    var connection = function () {

        var me = {};
        var listeners = [];
        var oneListeners = [];

        me.isConnected = false;

        oneListeners.removeOne = function (listener) {
            var index = oneListeners.indexOf(listener);
            if(index!=-1)
                oneListeners.splice(index, 1);
        };

        var correlationId = 0;
        me.nextCorrelationId = function () {
            return correlationId++;
        };

        $rootScope.queuedMessages = [];

        me.listen = function (predicate, handler) {
            listeners.push({ p: predicate, h: handler });
        };

        me.listenOnce = function (predicate, timeout) {
            var deferred = $q.defer();
            deferred.done = false;
            var listener = { d: deferred, p: predicate };
            oneListeners.push(listener);
            if (timeout) {
                $timeout(function () {
                    if (!deferred.done)
                        deferred.reject('timeout');
                    oneListeners.removeOne(listener);
                }, timeout);
            }
            var promise = deferred.promise;
            promise.then(function (data) {
                deferred.done = true;
            });
            return promise;
        };

        var onopen = function () {
            console.log('onopen');
            $rootScope.websocketAvailable = true;
            me.isConnected = true;
            $rootScope.$$phase || $rootScope.$apply();
            if ($rootScope.queuedMessages) {
                for (var i = 0; i < $rootScope.queuedMessages.length; i++) {
                    ws.send(JSON.stringify($rootScope.queuedMessages[i]));
                }
                $rootScope.queuedMessages = null;
                $rootScope.$$phase || $rootScope.$apply();
            }
        };

        var onclose = function () {
            console.log('onclose');
            me.isConnected = false;
            $rootScope.websocketAvailable = false;
            $rootScope.$$phase || $rootScope.$apply();
            $rootScope.queuedMessages = $rootScope.queuedMessages || [];

            setTimeout(function () {
                ws = connect();
            }, 5000);
        };

        var onmessage = function (msg) {
            console.log('onmessage');
            var obj = JSON.parse(msg.data);
            for (var i = 0; i < listeners.length; i++) {
                var listener = listeners[i];
                if (listener.p(obj))
                    listener.h(obj);
            }
            var remove = [];
            for (var i = 0; i < oneListeners.length; i++) {
                var listener = oneListeners[i];
                if (listener.p(obj)) {
                    var o = obj;
                    listener.d.resolve(o);
                    remove.push(listener);
                }
            }
            for (var i = 0; i < remove.length; i++) {
                oneListeners.removeOne(remove[i]);
            }
        };

        var onerror = function () {
            console.log('onerror');
        };

        me.send = function (obj) {

            if ($rootScope.queuedMessages)
                $rootScope.queuedMessages.push(obj);
            else
                ws.send(JSON.stringify(obj));
        }

        var setHandlers = function (w) {
            w.onopen = onopen;
            w.onclose = onclose;
            w.onmessage = onmessage;
            w.onerror = onerror;
        };

        var connect = function () {
            console.log('connecting...');
            var w = new WebSocket(websocketUrl);
            setHandlers(w);
            return w;
        }

        var ws = connect();

        return me;
    };
    return connection();
}])
vtortola
  • 34,709
  • 29
  • 161
  • 263