programing

이 각도 개선소켓과 함께 사용하는 JS 공장.이오

lastmoon 2023. 4. 2. 11:48
반응형

이 각도 개선소켓과 함께 사용하는 JS 공장.이오

AngularJS에서 socket.io을 사용하고 싶습니다.다음 공장을 찾았습니다.

app.factory('socket', function ($rootScope) {
    var socket = io.connect();
    return {
        on: function (eventName, callback) {
            socket.on(eventName, function () {
                var args = arguments;
                $rootScope.$apply(function () {
                    callback.apply(socket, args);
                });
            });
        },
        emit: function (eventName, data, callback) {
            socket.emit(eventName, data, function () {
                var args = arguments;
                $rootScope.$apply(function () {
                    if (callback) {
                        callback.apply(socket, args);
                    }
                });
            })
        }
    };

컨트롤러에서는 다음과 같이 사용됩니다.

function MyCtrl($scope, socket) {
    socket.on('message', function(data) {
        ...
    });
};

문제는 컨트롤러가 방문할 때마다 다른 청취자가 추가되기 때문에 메시지가 수신되면 여러 번 처리된다는 것입니다.

socket.io과 AngularJS를 통합하는 더 나은 전략은 무엇일까요?

EDIT: 공장에서 아무것도 반환하지 않고 청취한 후 $rootScope를 사용할 수 있습니다.$190과 $190입니다.컨트롤러에 $1이 있지만, 좋은 솔루션이 아닌 것 같습니다.

EDIT2: 공장 출하 시 추가

init: function() {
            socket.removeAllListeners();
}

socket.io 를 사용하는 각 컨트롤러의 선두에서 호출합니다.

여전히 최선의 해결책은 아닌 것 같아요

컨트롤러가 파괴될 때마다 소켓청취기를 분리합니다. 묶어야 요.$destroy다음과 같이 합니다.

function MyCtrl($scope, socket) {
    socket.on('message', function(data) {
        ...
    });

    $scope.$on('$destroy', function (event) {
        socket.removeAllListeners();
        // or something like
        // socket.removeListener(this);
    });
};

자세한 내용은 angularjs 설명서를 참조하십시오.

하고 Scope를 으로 이 할 수 .$destroy브로드캐스트되는 경우 해당 스코프의 컨텍스트에서 추가된 청취자만 소켓에서 삭제합니다.주의: 다음 내용은 테스트되지 않았습니다.

// A ScopedSocket is an object that provides `on` and `emit` methods,
// but keeps track of all listeners it registers on the socket.
// A call to `removeAllListeners` will remove all listeners on the
// socket that were created via this particular instance of ScopedSocket.

var ScopedSocket = function(socket, $rootScope) {
  this.socket = socket;
  this.$rootScope = $rootScope;
  this.listeners = [];
};

ScopedSocket.prototype.removeAllListeners = function() {
  // Remove each of the stored listeners
  for(var i = 0; i < this.listeners.length; i++) {
    var details = this.listeners[i];
    this.socket.removeListener(details.event, details.fn);
  };
};

ScopedSocket.prototype.on = function(event, callback) {
  var socket = this.socket;
  var $rootScope = this.$rootScope;

  var wrappedCallback = function() {
    var args = arguments;
    $rootScope.$apply(function() {
      callback.apply(socket, args);
    });
  };

  // Store the event name and callback so we can remove it later
  this.listeners.push({event: event, fn: wrappedCallback});

  socket.on(event, wrappedCallback);
};

ScopedSocket.prototype.emit = function(event, data, callback) {
  var socket = this.socket;
  var $rootScope = this.$rootScope;

  socket.emit(event, data, function() {
    var args = arguments;
    $rootScope.$apply(function() {
      if (callback) {
        callback.apply(socket, args);
      }
    });
  });
};

app.factory('Socket', function($rootScope) {
  var socket = io.connect();

  // When injected into controllers, etc., Socket is a function
  // that takes a Scope and returns a ScopedSocket wrapping the
  // global Socket.IO `socket` object. When the scope is destroyed,
  // it will call `removeAllListeners` on that ScopedSocket.
  return function(scope) {
    var scopedSocket = new ScopedSocket(socket, $rootScope);
    scope.$on('$destroy', function() {
      scopedSocket.removeAllListeners();
    });
    return scopedSocket;
  };
});

function MyController($scope, Socket) {
  var socket = Socket($scope);

  socket.on('message', function(data) {
     ...
  });
};

수락된 답변에 코멘트를 추가하고 싶지만 할 수 없습니다.그래서 답장을 쓸게요.저도 같은 문제가 있었습니다만, 제가 찾은 가장 쉽고 간단한 해답은 여기 michaeljoser가 제공한 다른 게시물에서 찾을 수 있습니다.

편의를 위해 아래를 복사해 두겠습니다.

remove All Listeners를 공장에 추가하고(아래 참조), 각 컨트롤러에 다음 코드를 설정해야 합니다.

$scope.$on('$destroy', function (event) {
socket.removeAllListeners();
});

출고 시 소켓 업데이트:

var socket = io.connect('url');
    return {
        on: function (eventName, callback) {
            socket.on(eventName, function () {
                var args = arguments;
                $rootScope.$apply(function () {
                    callback.apply(socket, args);
                });
            });
        },
        emit: function (eventName, data, callback) {
            socket.emit(eventName, data, function () {
                var args = arguments;
                $rootScope.$apply(function () {
                    if (callback) {
                        callback.apply(socket, args);
                    }
                });
            })
        },
      removeAllListeners: function (eventName, callback) {
          socket.removeAllListeners(eventName, function() {
              var args = arguments;
              $rootScope.$apply(function () {
                callback.apply(socket, args);
              });
          }); 
      }
    };
});

덕분에 목숨이 건졌어요. 다른 사람에게 도움이 됐으면 좋겠어요!

다음과 같이 서비스 또는 공장에서 기능을 생성하십시오.

unSubscribe: function(listener) {
    socket.removeAllListeners(listener);
}

그 후 아래와 같은 "$seconds" 이벤트로 컨트롤러를 호출합니다.

$scope.$on('$destroy', function() {
    yourServiceName.unSubscribe('eventName');
});

그것으로 해결되었다

저는 이 글을 읽기 전에 비슷한 문제를 풀었어요.난 군대에서 다 했어

.controller('AlertCtrl', ["$scope", "$rootScope", "Socket", function($scope, $rootScope, Socket) {
    $scope.Socket = Socket;
}])

// this is where the alerts are received and passed to the controller then to the view
.factory('Socket', ["$rootScope", function($rootScope) {
    var Socket = {
        alerts: [],
        url: location.protocol+'//'+location.hostname+(location.port ? ':'+location.port: ''),
        // io is coming from socket.io.js which is coming from Node.js
        socket: io.connect(this.url)
    };
    // set up the listener once
    // having this in the controller was creating a
    // new listener every time the contoller ran/view loaded
    // has to run after Socket is created since it refers to itself
    (function() {
        Socket.socket.on('get msg', function(data) {
            if (data.alert) {
                Socket.alerts.push(data.alert);
                $rootScope.$digest();
            }
        });
    }());
    return Socket;
}])

다른 방법을 시도했지만 예상대로 되지 않았다.★★★★★★★를 사용하고 있습니다.socket되어 있습니다.MainController a. a. a.GameController. 했을 때, 나는 단지 '보다'가 생성한 GameController를 남겨주세요.MainControllerrunning을 할 수 없기 때문에removeAllListeners기능.대신, 내 안에 중복이 생기지 않도록 하는 더 좋은 방법을 찾았습니다.socket공장 출하 시:

app.factory('socket', function ($rootScope) {
  var socket = io.connect();

  function on(eventName, callback) {
    socket.on(eventName, function () {
      var args = arguments;

      $rootScope.$apply(function () {
        callback.apply(socket, args);
      });
    });

    // Remove duplicate listeners
    socket.removeListener(eventName, callback);
  }

  function emit(eventName, data, callback) {
    socket.emit(eventName, data, function () {
      var args = arguments;

      $rootScope.$apply(function () {
        if (callback) {
          callback.apply(socket, args);
        }
      });
    });

    // Remove duplicate listeners
    socket.removeListener(eventName, callback);
  }

  return {
    on: on,
    emit: emit
  };
}

app.factory를 실행하는 대신 다음과 같은 서비스(싱글톤)를 만듭니다.

var service = angular.module('socketService', []);
service.factory('$socket', function() {
    // Your factory logic
});

그런 다음 $rootScope와 같이 단순히 앱에 서비스를 주입하고 컨트롤러에서 사용할 수 있습니다.

다음은 이 설정을 수행하는 방법에 대한 자세한 예입니다.

// App module
var app = angular.module('app', ['app.services']);

// services
var services = angular.module('app.services', []);

// Socket service
services.factory('$socket', ['$rootScope', function(rootScope) {

    // Factory logic here

}]);

// Controller
app.controller('someController', ['$scope', '$socket', function(scope, socket) {

    // Controller logic here

}]);

위의 Brandon의 답변에 대해 자세히 설명하자면, 저는 1)와 같은 각진 태그를 추가로 제거하는 서비스를 만들었습니다.$$hashKey는 요소에 남습니다.그리고 2) 소켓소프('..').on(')과 같은 이름붙인 소켓을 사용할 수 있습니다.'

(function (window, app, undefined) {
    'use strict';


    var ScopedSocket = function (socket, $rootScope) {
        this.socket = socket;
        this.$rootScope = $rootScope;
        this.listeners = [];
        this.childSockets = [];
    };

    ScopedSocket.prototype.removeAllListeners = function () {
        var i;

        for (i = 0; i < this.listeners.length; i++) {
            var details = this.listeners[i];
            this.socket.removeListener(details.event, details.fn);
        }

        for (i = 0; i < this.childSockets.length; i++) {
            this.childSockets[i].removeAllListeners();
        }
    };

    ScopedSocket.prototype.on = function (event, callback) {
        var socket = this.socket;
        var $rootScope = this.$rootScope;

        this.listeners.push({event: event, fn: callback});

        socket.on(event, function () {
            var args = arguments;
            $rootScope.$apply(function () {
                callback.apply(socket, args);
            });
        });
    };

    ScopedSocket.prototype.emit = function (event, data, callback) {
        var socket = this.socket;
        var $rootScope = this.$rootScope;

        socket.emit(event, angular.fromJson(angular.toJson(data)), function () {
            var args = arguments;
            $rootScope.$apply(function () {
                if (callback) {
                    callback.apply(socket, args);
                }
            });
        });
    };

    ScopedSocket.prototype.of = function (channel) {
        var childSocket = new ScopedSocket(this.socket.of(channel), this.$rootScope);

        this.childSockets.push(childSocket);

        return childSocket;
    };


    app.factory('Socket', ['$rootScope', function ($rootScope) {
        var socket = $rootScope.socket;

        return function(scope) {
            var scopedSocket = new ScopedSocket(socket, $rootScope);
            scope.$on('$destroy', function() {
                scopedSocket.removeAllListeners();
            });
            return scopedSocket;
        };
    }]);
})(window, window.app);

저는 아래 코드 같은 것을 사용하고 있습니다.sockets Service는 한 번만 인스턴스화되며 Angular는 GC를 $on으로 처리합니다.

$broadcast/$on이 마음에 들지 않는 경우 Angular를 위한 메시지 버스 구현이 약간 더 있습니다.

app.service('socketsService', ['$rootScope', function ($rootScope) {
    var socket = window.io.connect();

    socket.on('info', function(data) {
        $rootScope.$broadcast("info_received", data);
    });

    socket.emit('ready', "Hello");
}]);

app.controller("infoController",['$scope',
    function ($scope) {
        $scope.$root.$on("info_received", function(e,data){
            console.log(data);
        });
        //...
    }]);

app.run(
    ['socketsService',
        function (socketsService) {
        //...
    }]);

나는 청취자가 이미 존재하는지 확인함으로써 이 문제를 해결했다.여러 컨트롤러가 동시에 로딩되어 있는 경우(모두 소켓을 사용하는 다른 페이지모듈을 생각할 수 있습니다).IO)에서 등록된 모든 청취자를 삭제합니다.$destroy파괴된 컨트롤러와 아직 로딩되어 있는 모든 컨트롤러의 기능이 파손됩니다.

app.factory("SocketIoFactory", function ($rootScope) {
    var socket = null;
    var nodePath = "http://localhost:12345/";

    function listenerExists(eventName) {
        return socket.hasOwnProperty("$events") && socket.$events.hasOwnProperty(eventName);
    }

    return {
        connect: function () {
            socket = io.connect(nodePath);
        },
        connected: function () {
            return socket != null;
        },
        on: function (eventName, callback) {
            if (!listenerExists(eventName)) {
                socket.on(eventName, function () {
                    var args = arguments;
                    $rootScope.$apply(function () {
                        callback.apply(socket, args);
                    });
                });
            }
        },
        emit: function (eventName, data, callback) {
            socket.emit(eventName, data, function () {
                var args = arguments;
                $rootScope.$apply(function () {
                    if (callback) {
                        callback.apply(socket, args);
                    }
                });
            })
        }
    };
});

이는 어느 컨트롤러에 의해 등록된 청취자를 추적하고 파괴된 컨트롤러에 속하는 청취자만 삭제하여 메모리를 청소함으로써 더욱 개선될 수 있습니다.

중복되는 청취자를 피하기 위해 이 작업을 하고 있으며 상당히 잘 작동합니다.

 on: function (eventName, callback) {
  //avoid duplicated listeners
  if (listeners[eventName] != undefined) return;

  socket.on(eventName, function () {
     var args = arguments;
     $rootScope.$apply(function () {
        callback.apply(socket, args);
     });
     listeners[eventName] = true;
  });
},

브라우저를 새로 고친 후에도 동일한 이벤트가 중복되는 문제가 발생했습니다.'공장'을 사용하다가 '서비스'로 전환했습니다.socket.io 래퍼입니다.

myApp.service('mysocketio',['$rootScope', function($rootScope)
{
    var socket = io.connect();

    return {

        on: function(eventName, callback )
        {
            socket.on(eventName, function()
            {
                var args=arguments;
                $rootScope.$apply(function()
                {
                    callback.apply(socket,args);
                });
            });
        },

        emit: function(eventName,data,callback)
        {
            socket.emit(eventName,data,function()
            {
                var args=arguments;
                $rootScope.$apply(function()
                {
                    if(callback)
                    {
                        callback.apply(socket,args);
                    }
                });
            });
        }
    }

}]);

컨트롤러 내에서 이 서비스를 사용하여 이벤트를 듣습니다.

myApp.controller('myController', ['mysocketio', function(mysocketio)
{
    mysocketio.on( 'myevent', function(msg)
    {
        console.log('received event: ' + msg );
    }
}]);

공장에서 서비스 사용으로 전환한 후 브라우저 새로 고침 후 중복이 발생하지 않습니다.

Angular App에서 위의 코드를 사용하여 시도해보니 이벤트가 중복되어 있습니다.SocketIoFactory를 사용한@pootzko의 같은 예

추가했습니다.unSubscribe(even_name)내부$destroysocketEventListner를 삭제 또는 클리어합니다.

var app = angular.module("app", []);
..
..
..
//Create a SocketIoFactory
app.service('SocketIoFactory', function($rootScope){

    console.log("SocketIoFactory....");
    //Creating connection with server
    var protocol = 'ws:',//window.location.protocol,
        host = window.location.host,
        port = 80,
        socket = null;
    var nodePath = protocol+'//'+host+':'+port+'/';

    function listenerExists(eventName) {
        return socket.hasOwnProperty("$events") && socket.$events.hasOwnProperty(eventName);
    }

    return {
        connect: function () {
            socket = io.connect(nodePath);
            console.log('SOCKET CONNECTION ... ',nodePath);
        },
        connected: function () {
            return socket != null;
        },
        on: function (eventName, callback) {
            if (!listenerExists(eventName)) {
                socket.on(eventName, function () {
                    var args = arguments;
                    $rootScope.$apply(function () {
                        callback.apply(socket, args);
                    });
                });
            }
        },
        emit: function (eventName, data, callback) {
            socket.emit(eventName, data, function () {
                var args = arguments;
                $rootScope.$apply(function () {
                    if (callback) {
                        callback.apply(socket, args);
                    }
                });
            })
        },
        unSubscribe: function(listener) {
            socket.removeAllListeners(listener);
        }
    };
});

..
..
..

//Use in a controller
app.controller("homeControl", ['$scope', 'SocketIoFactory', function ($scope, SocketIoFactory) {

  //Bind the events
  SocketIoFactory.on('<event_name>', function (data) {

  });

  //On destroy remove the eventListner on socketConnection
   $scope.$on('$destroy', function (event) {
        console.log('[homeControl] destroy...');
        SocketIoFactory.unSubscribe('<event_name>');
    });
}]);

언급URL : https://stackoverflow.com/questions/14389049/improve-this-angularjs-factory-to-use-with-socket-io

반응형