Как отменить обнаружение изменений для especifc метода WebSocket?
Контекст: я использую веб-сокет для подключения к нетти-серверу для моего сайта. У меня есть собственные методы, и способ обработки сообщений также является индивидуальным.
Если сообщение, которое я хочу отправить, слишком большое, я разделю его на множество маленьких сообщений, чтобы один сокет мог отправлять "параллельные" сообщения. Эта часть работает нормально
Проблема: Каждый раз, когда "часть сообщения" достигает веб-сокета, она переходит в "MessageDecoder" для декодирования и объединения в реальное сообщение, а зоны (я полагаю).js запускает обнаружение изменения всей страницы. Поскольку это происходит так же быстро, как веб-сокет может обрабатывать части сообщения, это замедляет все приложение для обхода, что еще хуже, ничего в приложении никогда не изменяется, пока не будет завершено все сообщение и ТО не вызваны обратные вызовы (которые вызывают перемены). Загрузка файла обычно приводит к 6000 частям сообщения, где только последняя из них может надеяться внести какие-либо изменения в приложение.
Я уже пытался вслепую добавить 'this.zone.runOutsideAngular(() => {' к коду, но я не мог это исправить. Я не совсем понимаю, где это применимо, поскольку я не совершенно ясно, когда zone.js решает инициировать обнаружение изменений. В приведенном ниже коде будет комментарий, который указывает ЕДИНСТВЕННЫЙ фрагмент кода, который должен КОГДА-ЛИБО запускать обнаружение изменений.
OBS: Я мог бы позже добавить счетчик "сообщение с идентификатором zzz получил xxx счетчика частей yyy" (чтобы создать панель загрузки), так что это также должно было бы вызвать обнаружение изменений. Но, надеюсь, возможно гарантировать, что только те, кто слушает этот метод / переменную, будут проверять изменения, а не всю страницу
Код:
import { Injectable, NgZone } from '@angular/core';
declare var MessageDecoder: any;
declare var MessageEncoder: any;
declare var GetMessageTranslator: any;
declare var TextDecoder: any;
declare var DataStream: any;
declare var StatusCheck: any;
@Injectable()
export class ConnectionService {
public connection: any;
private status: boolean;
public ping: number;
public errorState: number;
public interval: number;
constructor(private zone: NgZone) {
this.startAutoConnect();
}
public getState() {
if (this.connection == undefined) {
return "offline";
}
if (this.errorState > new Date().getTime()) {
return "error";
}
if (this.connection.readyState != WebSocket.OPEN) {
return "offline";
} else {
if (this.status) {
return "loggedIn";
} else {
return "connected";
}
}
}
autoConnectHandle: any;
waitForSocketConnection(socket, callback) {
setTimeout(
function() {
if (socket.readyState === 1) {
if (callback !== undefined) {
callback();
}
return;
} else {
this.waitForSocketConnection(socket, callback);
}
}, 5);
};
onOpen1 = function() {
this.aThis.onOpen(this.aThis);
}
onOpen = function() {
this.waitForSocketConnection(this.connection, () => {
clearInterval(this.autoConnectHandle);
this.autoConnectHandle = undefined;
var connection: any = this.connection;
var aThis: ConnectionService = this;
connection.onclose = function() {
aThis.startAutoConnect();
}
connection.supersend = connection.send;
connection.send = function(message, callback, type) {
if (message.ID == undefined) {
message.configure();
message.init(this);
}
if (this.promiseMap[message.getRequestID()] == undefined) {
this.promiseMap[message.getRequestID()] = {
type: type,
callback: callback,
complete: false
};
}
message.writeToChannel(this);
}
connection.attr = {};
connection.promiseMap = {};
connection.onerror = function(error) {
this.errorState = new Date().getTime() + 1500;
console.error('WebSocket Error ' + error);
};
connection.channelRead = function(message) {//called by the message decoder after the message is complete
console.log("got: " + message.getClass());
var promisse = this.promiseMap[message.getRequestID()];
if (promisse != undefined) {
if (promisse.complete) {
return;
} else {
if (promisse.type == message.getClass() || promisse.type == undefined) {
if (promisse.callback != undefined) {
//THIS SHOULD TRIGGER THE CHANGE DETECTION, ANYTHING ELSE MEANS THAT IT WAS
//A MESSAGE THAT DON'T CARE ABOUT THE RESULT, SO NOTHING WILL CHANGE BECAUSE
//OF IT
promisse.callback(message);
}
promisse.complete = true;
delete this.promiseMap[message.getRequestID()];
} else if (message.getClass() == "NullServerMessage") {
}
}
var answer = message.processAnswer(this);
if (answer.getClass() != "NullServerMessage") {
console.log("sent: " + answer.getClass());
this.send(answer);
}
}
}
connection.decoder = new MessageDecoder();
connection.encoder = new MessageEncoder();
connection.onmessage = (e) => {
//I believe this is triggering the excess of change detections
var arrayBuffer;
var fileReader = <any>new FileReader();
fileReader.onload = function() {
connection.decoder.decode(connection, new DataStream(this.result), undefined)
};
fileReader.readAsArrayBuffer(e.data);
};
aThis.interval = setInterval(() => new function() {
connection.pingCounter = new Date().getTime();
connection.send(new StatusCheck(), function(message) {
aThis.status = message.status;
aThis.ping = new Date().getTime() - connection.pingCounter;
});
}, 1000);
connection.send(new GetMessageTranslator(), function(message) {
connection.decoder.translator = message.map;
connection.encoder.translator = message.map;
connection.encoder.translator.getKeyByValue = function(value) {
for (var prop in this) {
if (this.hasOwnProperty(prop)) {
if (this[prop] === value)
return prop;
}
}
}
});
});
}
startAutoConnect() {
if (this.connection) {
this.connection.close();
if (this.interval != undefined) {
clearInterval(this.interval);
this.interval = undefined;
}
this.connection = undefined;
}
var connection: any;
var aThis: ConnectionService = this;
if (!this.autoConnectHandle) {
this.autoConnectHandle = setInterval(() => new function() {
if (this.connection == undefined) {
aThis.connection = connection = new WebSocket('ws://localhost:80/websocket');
connection.aThis = aThis;
connection.onopen = aThis.onOpen1;
connection.onerror = function(error) {
aThis.startAutoConnect();
};
}
}, 1500);
}
}
}
1 ответ
Я думаю, что я понял, изменив
constructor(private zone: NgZone) {
this.startAutoConnect();
}
в
constructor(private zone: NgZone) {
this.zone.runOutsideAngular(() => {
this.startAutoConnect();
});
}
Это сделано для того, чтобы ничего не обновлялось в ответ на вызовы веб-сокетов, кроме счетчика ping, а результат от моих сообщений websocket обновлялся только после обновления счетчика ping, что заставляет меня думать, что это действительно работает, сообщения не вызывали Обнаружение изменений, Сорта, как я хотел.
Проблема заключается в том, что первоначальная проблема, связанная с тем, что во время загрузок все происходит очень медленно, сохраняется, поэтому это никогда не было причиной.
изменения
if (promisse.type == message.getClass() || promisse.type == undefined) {
if (promisse.callback != undefined) {
//THIS SHOULD TRIGGER THE CHANGE DETECTION, ANYTHING ELSE MEANS THAT IT WAS
//A MESSAGE THAT DON'T CARE ABOUT THE RESULT, SO NOTHING WILL CHANGE BECAUSE
//OF IT
promisse.callback(message);
}
promisse.complete = true;
delete this.promiseMap[message.getRequestID()];
в
if (promisse.type == message.getClass() || promisse.type == undefined) {
if (promisse.callback != undefined) {
this.aThis.zone.run(() => {
promisse.callback(message);
});
}
promisse.complete = true;
delete this.promiseMap[message.getRequestID()];
обнаружение изменений происходило каждый раз, когда сообщение было завершено, а обратный вызов завершил обработку.