123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207 |
- // Chat Homework
- // Этапы.
- // Используя функцию jsonPost на адрес http://students.a-level.com.ua:10012 напишите чат-клиент, который:
- // Stage 0
- // Для поиграться скопируйте в консоль функцию jsonPost (или запустите её с этой страницы) и вызовите её с теми или иными
- // объектами в качестве второго параметра. см. RPC.
- // jsonPost('http://students.a-level.com.ua:10012', {func: 'addMessage', nick: 'Anon', message: 'Я не умею копипастить в консоль, зато умею жать красную кнопку.'})
- // Если после объявления функции jsonPost запустить пример выше, то вы напишите в чат.
- // Stage 1
- // Отправляет сообщения в чат. Для проверки отслеживайте приходящий с сервера nextMessageId, который должен увеличиваться.
- // Интерфейс: поле ввода ника, поле ввода сообщения, кнопка отправки.
- // Stage 2
- // Читает все сообщения из чата и выводит их в отдельном контейнере.
- // Интерфейс: некий div-контейнер, в котором на каждое сообщение создается div (или иной блочный контейнерный тэг)
- // и в него помещается ник и сообщение. Также там есть timestamp который может показывать время отправки сообщения.
- // Stage 3
- // Читает только новые сообщения из чата. Для этого надо после каждого получения запоминать nextMessageId и отправлять его
- // при следующем запросе новых сообщений. Изначально nextMessageId равен нулю, что бы вычитать всю историю сообщений с сервера.
- // Stage 4
- // Делаем Stage 3 в setInterval для периодической проверки сообщений (раз в 2 -5 секунд).
- // Stage 5
- // Напишите асинхронную функцию отправки, которая внутри себя будет делать два запроса: на отправку и на проверку, для того
- // что бы минимизировать задержку между отправкой сообщения пользователя и его появлением в окне чата.
- // Оформите отдельно функции отправки и проверки новых сообщений как асинхронные (async, возвращает Promise):
- // async function sendMessage(nick, message) отсылает сообщение.
- // async function getMessages() получает сообщения и отрисовывает их в DOM
- // async function sendAndCheck() использует две предыдущие для минимизации задержки между отправкой сообщения и приходом их.
- // Именно эта функция должна запускаться по кнопке.
- // async function checkLoop() использует delay и бесконечный цикл для периодического запуска getMessages().
- // Stage 6
- // Прогуглить и разобраться с fetch и заменить внутренности jsonPost на код, использующий fetch вместо XMLHttpRequest.
- // Информация
- // jsonPost
- // Данная промисифицированная функция (кстати, сделайте из неё асинхронную) умеет общаться с моим чат-сервером отправляя
- // RPC запросы используя JSON. Remote Procedure Call - вызов функций удаленно, имя функции и параметры передаются AJAX-ом
- // в формате (например) JSON.
- // function jsonPost(url, data)
- // {
- // return new Promise((resolve, reject) => {
- // var x = new XMLHttpRequest();
- // x.onerror = () => reject(new Error('jsonPost failed'))
- // //x.setRequestHeader('Content-Type', 'application/json');
- // x.open('POST', url, true);
- // x.send(JSON.stringify(data))
- // x.onreadystatechange = () => {
- // if (x.readyState == XMLHttpRequest.DONE && x.status == 200){
- // resolve(JSON.parse(x.responseText))
- // }
- // else if (x.status != 200){
- // reject(new Error('status is not 200'))
- // }
- // }
- // })
- // }
- // Первым параметром указывается URL (см. выше) на который отправляется методом POST с JSON, вторым - готовый к JSONификации
- // объект, который вы хотите отправить на сервер.
- // RPC
- // addMessage
- // Если вы отправите подобный JSON на сервер (см jsonPost), то сервер запишет ваше сообщение во внутренний массив и отдаст
- // новую длину массива. После этого другие могут прочесть это сообщение, используя метод getMessages.
- // {func: 'addMessage', nick: 'msg', message: 'msg'}
- // Поле func является обязательным и содержит имя функции, которая должна быть вызвана на сервере. Функция на сервере получает
- // параметром остальной объект (поля nick и message) В ответ вы получите так же объект:
- // {nextMessageId: 100500}
- // где 100500: новая длина массива сообщений на сервере после добавления вашего сообщения.
- // getMessages
- // Позволяет прочесть часть массива сообщений от отпределенного индекса и до конца.
- // Используется для последовательной дочитки новых сообщений. Для этого надо передать в jsonPost подобный объект:
- // {func: 'getMessages', messageId: 0}
- // При значении 0 в messageId сервер отдаст сообщения от 0 до конца, т. е. весь массив:
- // {
- // 'data': [
- // {
- // 'nick': 'test',
- // 'message': 'test',
- // 'timestamp': 1524225450317
- // },
- // {
- // 'nick': 'test',
- // 'message': 'test2',
- // 'timestamp': 1524225460973
- // },
- // {
- // 'nick': 'test',
- // 'message': 'test3',
- // 'timestamp': 1524225504849
- // },
- // {
- // 'nick': 'SirkoSobaka',
- // 'message': 'Hello!',
- // 'timestamp': 1524226323310
- // },
- // {
- // 'nick': 'SirkoSobaka',
- // 'message': 'Hello!',
- // 'timestamp': 1524226326628
- // },
- // ],
- // 'nextMessageId': 5
- // }
- // После чего вы итерируете по data и выводите каждый отдельный объект как DOM-элемент с текстом, и, возможно, вложенной
- // версткой (время в отдельном элементе и т.п.)
- // nextMessageId вы запоминаете для того, что бы отправить следующий запрос начиная с него (т. е. с 68 в примере выше).
- // Тогда сервер отдаст вам только сообщения новее последнего запроса.
- // Chat Server
- // Если у вас не чрезмерно свежий хром, то это можно делать даже на локалхосте и открывать файл из браузера с диска.
- // Сервер позволяет соединятся с собой с любого домена.
- // Удачи :-)
- const url = 'http://students.a-level.com.ua:10012';
- const nick = document.getElementById('nick');
- const message = document.getElementById('message');
- const btnSendMessage = document.getElementById('button');
- const container = document.getElementById('container');
- let nextMessageId = 1;
- let delay = 1000;
- setInterval(getMessages, delay);
- function getMessages() {
- jsonPost(url, { func: 'getMessages', messageId: nextMessageId })
- .then((response) => response.json())
- .then((data) => {
- nextMessageId = data.nextMessageId;
- updateMessages(data.data);
- });
- }
- function jsonPost(url, data) {
- return fetch(url, {
- method: 'POST',
- body: JSON.stringify(data),
- });
- }
- function updateMessages(messageList) {
- messageList.forEach((msg) => {
- newGotMessage(msg);
- });
- }
- function newGotMessage({ nick, message, timestamp }) {
- const newBlock = document.createElement('div');
- newBlock.classList.add('block');
- const time = new Date(timestamp);
- let html = '';
- html += `<p>${nick}<p>`;
- html += `<p class='message'>'${message}'</p>`;
- html += `<p>${time.toString()}</p>`;
- newBlock.innerHTML = html;
- container.prepend(newBlock);
- }
- btnSendMessage.addEventListener('click', sendAndCheck);
- async function sendAndCheck() {
- await sendMessage();
- getMessages();
- message.value = '';
- }
- async function sendMessage() {
- jsonPost(url, {
- func: 'addMessage',
- nick: nick.value,
- message: message.value,
- });
- }
|