Question

I have a service wrapped around WebSocket, I wanted to do it with promises and coupling requests with responses, here is what I came up with:

(function () {

var app = angular.module('mainModule');

app.service('$wsService', ['$q', '$rootScope', '$window', function($q, $rootScope, $window) {

    var self = this;

    // Keep all pending requests here until they get responses
    var callbacks = {};
    // Create a unique callback ID to map requests to responses
    var currentCallbackId = 0;

    var ws = new WebSocket("ws://127.0.0.1:9090");
    this.webSocket = ws;

    ws.onopen = function(){
        $window.console.log("WS SERVICE: connected");
    };

    ws.onmessage = function(message) {
        listener(JSON.parse(message.data));
    };

    var listener = function (messageObj) {
        // If an object exists with callback_id in our callbacks object, resolve it
        if(callbacks.hasOwnProperty(messageObj.Request.ID)) {
            $rootScope.$apply(
                callbacks[messageObj.Request.ID].cb.resolve(messageObj));
            delete callbacks[messageObj.Request.ID];
        }
    };

    // This creates a new callback ID for a request
    var getCallbackId = function () {
        currentCallbackId += 1;
        if(currentCallbackId > 10000) {
            currentCallbackId = 0;
        }
        return currentCallbackId;
    };

    //sends a request
    var sendRequest = function (request, callback) {
        var defer = $q.defer();
        var callbackId = getCallbackId();
        callbacks[callbackId] = {
            time: new Date(),
            cb:defer
        };
        request.ID = callbackId;

        $window.console.log("WS SERVICE: sending " + JSON.stringify(request));
        ws.send(JSON.stringify(request));

        if(typeof callback === 'function') {
            defer.promise.then(function(data) {
                callback(null, data);
            },
            function(error) {
                callback(error, null);
            });
        }
        return defer.promise;
    }; 

    this.exampleCommand = function(someObject, callback){
        var promise = sendRequest(someObject, callback);
        return promise;
    };
}]);

}());

And I use it in a controller like so:

(function () {
'use strict';

var app = angular.module('mainModule');

app.controller('someController', ['$scope', '$wsService', function ($scope, $wsService) {

    $scope.doSomething = function(){
        $wsService.exampleCommand(
            {/*some data for the request here*/},
            function(error, message){
                //do something with the response
            }
        );    
    };

}]);

}());

After implementing this, I have been told that the service should not really operate on any kind of scope. So my question is - how would I go about removing the $rootScope from the service? I am not even sure if I should get rid of it, and if the conventions say I should, how to approach it. Thanks

Was it helpful?

Solution

I have been told that the service should not really operate on any kind of scope.

Who told you that? It's completely wrong.

Your service is receiving callbacks outside of a digest cycle from the websocket. To work with angular, those updates need to be applied inside a digest cycle - this is exactly what you're doing.

For reference, see the built in $http service. That wraps XMLHttpRequest analogously to how you're wrapping web sockets and it depends on $rootScope for exactly the functionality your code depends on $rootScope for.

Your code demonstrates the canonical use of $rootScope inside a service.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top