123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223 |
- //CHAT
- // -------------УСЛОВИЕ-------------
- // Этапы.
- // Используя функцию 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 вы запоминаете для того, что бы отправить следующий запрос начиная с него(т.е.с 5 в примере выше).
- // Тогда сервер отдаст вам только сообщения новее последнего запроса.
- // Chat Server
- // Можно глянуть тут: сервер Обратите внимание на массив messages и на функции в ассоциативном массиве RPCFuncs.
- // А также на эти строки кода
- // Спойлер
- // чатик
- // Но вам все равно надо переписать это на async, await, Promise и DOM.
- // Где это делать?
- // Если у вас не чрезмерно свежий хром, то это можно делать даже на локалхосте и открывать файл из браузера с диска.
- // Сервер позволяет соединятся с собой с любого домена.
- // -------------РЕШЕНИЕ-------------
- // Stage 0
- let lastMessageId = 0;
- // 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'))
- // }
- // }
- // })
- // };
- // Stage 1
- // const sendMessage = async (nick, message) => {
- async function sendMessage(nick, message){
- let res = await jsonPost("http://students.a-level.com.ua:10012", {
- func: 'addMessage', nick: nick,
- message: message
- })
- console.log(res.nextMessageId);
- }
- send.onclick = () => {
- let nickInput = nick.value;
- let msgInput = msg.value;
- if (nickInput && msgInput) { sendAndCheck(nickInput, msgInput) }
- msg.value = '';
- };
- // Stage 2
- async function getMessages(){
- let res = await jsonPost("http://students.a-level.com.ua:10012", {
- func: 'getMessages', messageId: lastMessageId
- })
- lastMessageId = res.nextMessageId;
- historyDraw(res.data);
- }
- //Отрисовывает данные
- function historyDraw(array) {
- let history = array.reverse().map(item => `<div class='postWrapper'><div class='timeWrapper'>${dateTransform(item.timestamp)}</div> <b>${item.nick}:</b>  ${item.message}</div>`);
- chat.insertAdjacentHTML('afterbegin', history.join(''));
-
- };
- //Приводит дату к нужному виду
- function dateTransform(date) {
- date = new Date(date);
- return ((date.getHours() < 10 ? ('0' + date.getHours() ) : date.getHours()) + ':' + (date.getMinutes() < 10 ? ('0' + date.getMinutes() ) : date.getMinutes()) + ' ' + (date.getDate() < 10 ? ('0' + (date.getDate())) : date.getDate() )+ '.' + ((date.getMonth() + 1) < 10 ? ('0' + (date.getMonth() + 1)) : (date.getMonth() + 1)) + '.' + date.getFullYear());
- }
-
- // Stage 4
- // setInterval(() => getMessages(lastMessageId), 5000);
-
- // Stage 5
- async function sendAndCheck(nick, message) {
- sendMessage(nick, message);
- getMessages();
- }
- const delay = ms => new Promise(ok => setTimeout(() => ok(ms), ms));
- // async function checkLoop() { setInterval(() => getMessages(lastMessageId), 5000); }
- async function checkLoop() {
- while (true) {
- await delay(5000);
- getMessages(lastMessageId);
- }
- }
- checkLoop();
- // Stage 6
- async function jsonPost(url, data) {
- let response=await fetch(url, {
- method: 'POST',
- body: JSON.stringify(data) })
-
- if (response.ok) {
- return await response.json();
- } else {
- alert("Ошибка HTTP: " + response.status);
- }
- };
-
-
|