Wednesday, March 12, 2014

AngularJS, SignalR and promises

Recently I have started to play a bit with AngularJS and SignalR. When I was developing first, simple solution I run into interesting problem.

According to the documentation of SignalR, all methods from the proxy should return promises. Unfortunately, when I was trying to use them, application was not behaving as it should.

I have created following, very simple service, which is downloading some configuration data from hub, them getting all required values and starting to update data when all is done


(function () {
'use strict';

var serviceId = 'signalRSvc';
angular.module('app').service(serviceId, ['$rootScope', signalrcontext]);

function signalrcontext( $rootScope) {
    var hub = null;
    var connection = null;        

    var service = {
        initialize: initialize,
        getInitialData: getInitialData,
        getAllValues: getAllValues
    };

    return service;

    function initialize() {
        connection = $.connection;

        hub = connection.webServiceHub;
        connection.hub.logging = true;

        hub.client.updateData = function(data) {
            $rootScope.$emit("updateData", data);
        };

        return connection.hub.start();
    };

    function getInitialData() {
        return hub.server.getInitialData();
    };

    function getAllValues(id) {
        return hub.server.getAllValues(id);
    };
}
})();


Problem started when I tried to execute all functions in right order to get the data


    function initSignalR() {
        return signalRSvc.initialize().then(function() {                
            return signalRSvc.getInitialData();
        }).then(function(configurations) {            
            return getAllValues(configurations);
        }).then(function (resultData) {
            angular.forEach(chartData, function(value, key) {
                //do something usefull when all is initialized
            };
        });
        });
    }

    function getAllValues(configurations) {
        var promises = new Array();

        angular.forEach(configurations, function(configuration) {
            promises.push(signalRSvc.getAllValues(configuration.Id));
        });

        return $q.all(promises);
    }


It looked like $q.all was not waiting for all promises to complete. Instead it was immediately calling last then with no data inside.

After some struggling, it turns out that promises in SingalR are not compatible with ones used by AngularJS. Fortunately, AngularJS team included method for such case $q.when which is able to wrap an object that might be a value or a (3rd party) then-able promise into a $q promise

It was enough to wrap all calls to SignalR methods, and everything is working fine now.

(function () {
'use strict';

var serviceId = 'signalRSvc';
angular.module('app').service(serviceId, ['$rootScope', signalrcontext]);

function signalrcontext( $rootScope) {
    var hub = null;
    var connection = null;        

    var service = {
        initialize: initialize,
        getInitialData: getInitialData,
        getAllValues: getAllValues
    };

    return service;

    function initialize() {
        connection = $.connection;

        hub = connection.webServiceHub;
        connection.hub.logging = true;

        hub.client.updateData = function(data) {
            $rootScope.$emit("updateData", data);
        };

        return $q.when(connection.hub.start());
    };

    function getInitialData() {
        return $q.when(hub.server.getInitialData());
    };

    function getAllValues(id) {
        return $q.when(hub.server.getAllValues(id));
    };
}
})();



Hopefully, this will save you some struggling.

1 comment: