Olga_Brekhuntsova vor 2 Jahren
Ursprung
Commit
c89884e1de

+ 104 - 0
hw-js-14-async-await/.gitignore

@@ -0,0 +1,104 @@
+# Logs
+logs
+*.log
+npm-debug.log*
+yarn-debug.log*
+yarn-error.log*
+lerna-debug.log*
+
+# Diagnostic reports (https://nodejs.org/api/report.html)
+report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json
+
+# Runtime data
+pids
+*.pid
+*.seed
+*.pid.lock
+
+# Directory for instrumented libs generated by jscoverage/JSCover
+lib-cov
+
+# Coverage directory used by tools like istanbul
+coverage
+*.lcov
+
+# nyc test coverage
+.nyc_output
+
+# Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files)
+.grunt
+
+# Bower dependency directory (https://bower.io/)
+bower_components
+
+# node-waf configuration
+.lock-wscript
+
+# Compiled binary addons (https://nodejs.org/api/addons.html)
+build/Release
+
+# Dependency directories
+node_modules/
+jspm_packages/
+
+# TypeScript v1 declaration files
+typings/
+
+# TypeScript cache
+*.tsbuildinfo
+
+# Optional npm cache directory
+.npm
+
+# Optional eslint cache
+.eslintcache
+
+# Microbundle cache
+.rpt2_cache/
+.rts2_cache_cjs/
+.rts2_cache_es/
+.rts2_cache_umd/
+
+# Optional REPL history
+.node_repl_history
+
+# Output of 'npm pack'
+*.tgz
+
+# Yarn Integrity file
+.yarn-integrity
+
+# dotenv environment variables file
+.env
+.env.test
+
+# parcel-bundler cache (https://parceljs.org/)
+.cache
+
+# Next.js build output
+.next
+
+# Nuxt.js build / generate output
+.nuxt
+dist
+
+# Gatsby files
+.cache/
+# Comment in the public line in if your project uses Gatsby and *not* Next.js
+# https://nextjs.org/blog/next-9-1#public-directory-support
+# public
+
+# vuepress build output
+.vuepress/dist
+
+# Serverless directories
+.serverless/
+
+# FuseBox cache
+.fusebox/
+
+# DynamoDB Local files
+.dynamodb/
+
+# TernJS port file
+.tern-port

+ 12 - 0
hw-js-14-async-await/.prettierrc.json

@@ -0,0 +1,12 @@
+{
+    "printWidth": 80,
+    "tabWidth": 2,
+    "useTabs": false,
+    "semi": true,
+    "singleQuote": true,
+    "trailingComma": "all",
+    "bracketSpacing": true,
+    "jsxBracketSameLine": false,
+    "arrowParens": "avoid",
+    "proseWrap": "always"
+  }

+ 3 - 0
hw-js-14-async-await/.vscode/settings.json

@@ -0,0 +1,3 @@
+{
+  "liveServer.settings.port": 5502
+}

+ 1 - 0
hw-js-14-async-await/README.md

@@ -0,0 +1 @@
+Хостинг http://hw_js_14.olgapistryak.fe.a-level.com.ua/

+ 27 - 0
hw-js-14-async-await/index.css

@@ -0,0 +1,27 @@
+* {
+  box-sizing: border-box;
+}
+
+body {
+  padding: 30px;
+}
+.inputWrapper {
+  display: flex;
+  align-items: flex-start;
+  margin-bottom: 10px;
+}
+#msg {
+  margin-left: 20px;
+}
+#send {
+  margin-left: 20px;
+}
+.timeWrapper {
+  font-size: 11px;
+  width: 90px;
+}
+.postWrapper {
+  display: flex;
+  margin-bottom: 10px;
+  align-items: baseline;
+}

+ 24 - 0
hw-js-14-async-await/index.html

@@ -0,0 +1,24 @@
+<!DOCTYPE html>
+<html>
+
+<head>
+  <meta charset="utf-8">
+  <meta name="viewport" content="width=device-width">
+  <title>hw-js-12-async-promise: CHAT</title>
+  <link href="index.css" rel="stylesheet" type="text/css" />
+  <script src="http://code.jquery.com/jquery-3.1.1.min.js"></script>
+</head>
+
+<body>
+  <div class="inputWrapper">
+  <input type='text' id='nick' placeholder='nick'>
+  <textarea type='text' id='msg' placeholder='message' style="height:100px;"></textarea>
+  <button id='send'>Send</button>
+  </div>
+  <div id="root"></div>
+  <div id='chat'>
+  </div>
+  <script src="/js/chat.js" ></script>
+</body>
+
+</html>

+ 223 - 0
hw-js-14-async-await/js/chat.js

@@ -0,0 +1,223 @@
+//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> &nbsp${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);
+}
+};
+ 
+