Та и не нашёл, где там модули! То, что вы запихнули файлы в папку `modules` и, о ужас, дописали к имени переменной "Module", конечно же не сделает из них ни каким автомагическим способом модули. (также они все глобальны у вас, но странности вида `const Chat = window.Chat;` тоже зачем-то встретились)
Чтобы указать входную точку для вашей программы, используйте атрибут type со значением module
<script src="/javascripts/main.js" type="module"></script>
И это единственное, что вам нужно указать в вашем html.
const chat = new Chat;
chat.init();
Так и представил, что было если было бы так:
const array = new Array;
array.init();
Так что почему бы не просто `const chat = new Chat;`?
Используйте стрелочные функции, тогда не придётся писать такое `const _t = this;`
Можно просто `event_name.constructor === String`
`this.socket.readyState === 1` WTF??? Почему не `this.socket.readyState === this.socket.OPEN` мне пришлось посмотреть в документации, что такое 1
У вас в SocketModule.prototype.readyStateConnection переменная timer всегда новая, так что старый таймер невожможно остановить
У вас в chat.js передается `path: options.socketPath`, но в socketModule.js не используется...
Вместо класса XMLHttpRequest можно использовать функцию fetch.
Ваш /api/connect может возвратить не только 200 но и 304 (может быть, у меня именно так и произошло), почему бы не проверять так `response.status >= 200 && response.status < 400`, или даже `response.ok` (если используете fetch)
`this.socket;`
почему бы не (однако это ужасно, кто знает как лучше?)
this.socket = {
readyState: WebSocket.prototype.CLOSED
};
тогда не будет неожиданных ошибок связанных тем, что this.socket равен null Это и была главная причина!
Вот исправленный код, исправленный с учётом сказанного выше, и модули тоже используются (а не переменные с именем, содержащим "Module")
В
index.html теперь только нужно, то что касается скриптов
<script src="/javascripts/main.js" type="module"></script>
main.js
import Chat from "./Chat.js";
document.addEventListener("DOMContentLoaded", () => {
const chat = new Chat;
});
options.js Я использовал этот сервер для теста, у вас там должно быть что-то другое
export default {
socketPath: "wss://echo.websocket.org"
};
Chat.js
import Socket from "./modules/socketModule.js";
import options from "./options.js";
export default class Chat {
constructor() {
this.transport = new Socket({
path: options.socketPath
});
// FIXME в отдельный модуль это я проверил как оно работает если текст в текстовое поле ввести и отправить
document.addEventListener("click", event => {
if(event.target.matches("#send_message [type='submit']")) {
event.preventDefault();
this.transport.sendMessage('chat_message', {
type: 'text',
text: document.querySelector("[name='message_text']").value
});
}
})
this.transport.sendMessage('chat_message', {
type: 'text',
text: 'Lorem ipsum dolor sit amet, consectetur adipisicing elit.'
});
}
}
modules/socketModule.js
export default class SocketModule {
constructor({ path }) {
this.socket = {
readyState: WebSocket.prototype.CLOSED
};
this.connection();
this.path = path;
}
connection() {
this.checkServer(server => {
if (!server) return;
this.socket = new WebSocket(this.path);
this.socket.addEventListener('open', () => {
this.sendMessage('connection', {
client: true
});
this.indicatorControl = true;
});
this.socket.addEventListener("message", event => {
// FIXME логику разбора в отдельный модуль
const { event_name, data } = JSON.parse(event.data);
if(event_name === "chat_message") {
var p = document.createElement("p");
p.textContent = data.text;
document.getElementById("messages").append(p);
}
});
this.socket.addEventListener('close', () => {
this.indicatorControl = false;
this.connection();
});
});
return this;
}
set indicatorControl(value) {
// что-то делает
}
checkServer(callback) {
let timer;
let count = 0;
timer = setInterval(function() {
fetch("/api/connect", {
headers: {
"Content-Type": "application/x-www-form-urlencoded"
}
}).then(response => {
if(response.ok) {
clearInterval(timer);
callback(true);
}
});
count++;
if (count === this.reconnectCount) {
clearInterval(timer);
console.log('Превышен лимит ожидания!');
}
}, 200);
return this;
}
sendMessage(event_name, data) { // event_name (string), data (string, array, object)
if (event_name.constructor === String) {
this.readyStateConnection(() => {
console.log(event_name, data);
this.socket.send(JSON.stringify({ event_name, data }));
});
} else
return;
return this;
}
readyStateConnection(callback) {
if (this.socket.readyState === this.socket.OPEN) {
callback();
} else {
setTimeout(() => {
this.readyStateConnection(() => callback());
}, 0);
}
}
};
Но очень там не сердитесь, что я всё исправил на модули, как описано в стандарте JavaScript.
UPD В readyStateConnection всё-таки не нужно стирать таймер, а то сообщения отправленные до подключения не отправятся. Исправил код выше(modules/socketModule.js)