AngularJS websockets太快了

时间:2014-06-28 00:22:01

标签: javascript angularjs websocket angularjs-scope

我正在使用AngularJS来支持我的webapp。最近我决定在与RabbitMQ的混合中引入Stomp over websockets。我一直遇到的一个问题是,有时从后端发送的消息的频率非常高,以至于Angular无法触发某些事件,而这些事件会在流程中丢失数据。

应用程序相当复杂,因此有时消息可能会通过以下链

  1. StompJS Angular服务包装器。使用$ rootScope。$ broadcast在新消息到达时通知组件
  2. 注册到$ rootScope上正在广播的消息的控制器。 Controller然后更新绑定或将另一个事件发送到组件以执行某些数据更新。
  3. 正如我提到的有时,范围无法正确更新,即使正在发送事件并且正在填充值,视图也不会显示更新的值。我已经使用了$ scope。$ apply,$ timeout,$ scope。$ digest似乎没什么用......例如,如果我有一个接一个的2个数据包,它们之间很少或没有延迟就没有任何事情发生但是如果有一个相同的代码片段正常运行。如何克服这个?

    在用户发布的一些问题之后进一步举例说明:

    我将采用最简单的示例:我从后端运行的作业获取进度报告。基本上它告诉我在Cassandra数据库中写了多少行。所以我收到了像{write:8000,total:4000000}这样的通知。当工作人员在数据库中写入时,“写入”正在增加,并且由于有多个工作人员,有时这些通知会被推得非常快。我编写了一个自定义网格组件,用于侦听grid:updateCell等事件(允许我使用进度报告更新单元格)。每次数据包进入套接字时,我都会广播该事件($ scope。$ broadcast,因为它是控制器页面的子网格)。我注意到,虽然事件被捕获并且网格事件也是在成功更新数据模型但不是UI

    的情况下触发的,但套接字中的所有更新都不会反映在UI中

2 个答案:

答案 0 :(得分:3)

在我看来,你可能会在这里发生一些事情。

1)使用$ scope。$ broadcast在示波器范围内冒泡,如果你有很多嵌套作用域,这可能会很重。我更喜欢将事件监听器附加到$ rootScope并使用$ emit,它只广播到当前作用域并且更快。我有以下服务

    /**
     * @ngdoc service
     * @name someModule.eventbus
     * @requires $rootScope
     *
     * @description
     * Provides a eventing mechanism when a user cna broadcast and subscribe to application wide events.
     */
    angular.module('someModule').factory('eventbus', [
        '$rootScope',
        function ($rootScope) {
            /**
             * @ngdoc function
             * @name subscribe
             * @methodOf someModule.eventbus
             *
             * @description
             * Subscribes a callback to the given application wide event
             *
             * @param {String} eventName The name of the event to subscribe to.
             * @param {Function} callback A callback which is fire when the event is raised.
             * @return {Function} A function tht can be called to unsubscrive to the event.
             */
            var subscribe = function (eventName, callback) {
                    return $rootScope.$on(eventName, callback);
                },

                /**
                 * @ngdoc function
                 * @name broadcast
                 * @methodOf someModule.eventbus
                 *
                 * @description
                 * Broadcasts the given event and data.
                 *
                 * @param {String} eventName The name of the event to broadcast.
                 * @param {object} data A data object that will be passed along with the event.
                 */
                broadcast = function (eventName, data) {
                    $rootScope.$emit(eventName, data);
                };

            return {
                subscribe: subscribe,
                broadcast: broadcast
            };
        }
    ]);
  1. 您可能无法在Angular摘要周期内触发更新,这可以解释为什么您只获得一些更新。尝试使用摘要周期进行更新(将函数放在$ timeout块中,这是推荐的超过$ scope的方法。$ apply

    $ timeout(function(){   doUpdate(dataFromMessageBus); });

  2. 如果你收到很多信息,你应该使用去抖功能,请参阅lodash-debouce。一个人只能在大约200毫秒的时间内处理,所以一般每200毫秒更新一次是最有人可以接受的。我会亲自每隔一秒左右更换一个交易应用程序等。

  3. (使用lodash - debounce)

    function (messageBus) {
      var debounceFn = _.debounce(function () {
          $timeout(function() {
            doUpdate(data);
          });
        }, 1000);
    
        // just assuming here about your socket / message bus stuff
        messageBus.onMessageRecieved(function () {
          //Call debounce function which will only actually call the inner func every 1000ms
          debounceFn();
       });
    
    }
    

答案 1 :(得分:1)

之前我遇到过同样的问题,我可以通过包裹websocket来解决它 传入的消息处理程序,它在下面更新我的Angular Grid:

$scope.$apply(function () {

//your code to update the Angular UI

});

示例:

    /**
     * @ngdoc function
     * @description
     * On Connect. Callback to subscribe to specific socket destination. 
     */
    function _subscribeBcQueue(){
        var uuid = SessionService.getUuid();
        var userBcQueue = CONFIG.SOCKET.bcQueue+'/'+uuid
        var subscription = $stomp.subscribe(userBcQueue, function (payload, headers, message) {
            _onBcMessageHandler(message)
        }, {})
    }

    /**
     * @ngdoc function
     * @description
     * OnBcMessageHandler. Callback function to handle the incoming direct message. 
     */
    function _onBcMessageHandler(message){
        //TODO Process the broadcasting status message
        var bcMsg = JSON.parse(message.body);
        console.log("Broadcasting status message:" + bcMsg);
        _updateBcJobGridRow(bcMsg)
    }



    function _updateBcJobGridRow(job) {
            $scope.$apply(function () {
                //Get the old JobGridRow 
                console.log("jobId: " + JSON.stringify(job.id));
                var oldJobGridRow = $filter('filter')(vm.gridOptions.data, {id: job.id})[0];
                console.log("oldJobGridRow: " + JSON.stringify(oldJobGridRow));
                if (oldJobGridRow) {
                    var newJobGridRow = _getBcJobGridRow(job);
                    console.log("newJobGridRow: " + JSON.stringify(newJobGridRow));
                    oldJobGridRow.progress = newJobGridRow.progress;
                    oldJobGridRow.percentage = newJobGridRow.percentage;
                    oldJobGridRow.status = newJobGridRow.status;
                    oldJobGridRow.total = newJobGridRow.total;
                    oldJobGridRow.sent = newJobGridRow.sent;
                    oldJobGridRow.recipients = newJobGridRow.recipients;
                }
            });
        }