Результати пошуку
ITVDN: курси програмування
Відеокурси з
програмування

Замовити дзвінок

Вибери свою IT спеціальність

Підписка

Замовити дзвінок

+38 099 757 27 82
Результати пошуку за запитом: Обучение c
Асинхронне програмування на JavaScript

Автор: Дмитро Охріменко

План: 1. Різниця між синхронним та асинхронним кодом 2. Багатозадачність процеси й потоки, у чому різниця 3. Особливості багатозадачності в JavaScript 4. Асинхронні операції на практиці HTTP-запит як найпоширеніший кейс 5. Підходи до написання асинхронного коду: Promise    Async/Await Observable 6. Практичні поради Різниця між синхронним та асинхронним кодом Для початку давайте визначимо ці два терміни: Синхронний код - це код, який виконується послідовно, функція за функцією. Асинхронний код - код, який може виконуватися паралельно: наступна функція запускається, не чекаючи завершення попередньої. Щоб провести аналогію з реального життя, уявімо кухаря. Якщо кухар працює синхронно, то поки він не завершить приготування однієї страви, не переходить до наступної. Але це неефективно й призводить до втрати часу. Якщо ж кухар діє асинхронно, то поки м’ясо запікається в духовці, а на плиті закипає вода, він нарізає овочі. Тобто він один, але не стоїть без діла - виконує інші задачі, поки щось готується саме. Уявімо, що кухар - це процесор. А запікання м’яса в духовці - це завантаження файлу з мережі. Кухар може просто стояти й дивитись, як м’ясо готується. А може нарізати овочі, перевіряти, чи не з’явились нові замовлення, або скролити стрічку в соцмережі. Так само і з програмами: поки мережева карта завантажує файл, процесор не мусить чекати - він може малювати інтерфейс, оновлювати прогрес-бар чи виконувати обчислення у фоні. Але для цього потрібно правильно написати код - так, щоб він міг працювати асинхронно. Код який виконується синхронно ```js console.log("Початок"); console.log("Дія"); сonsole.log("Кінець"); ``` Результат: Початок Дія Кінець   Код який виконується асинхронно. і ``js console.log("Початок"); setTimeout(() => { // за допомогою setTimeout ми відкладаємо запуск коду на певний час   console.log("Дія через 2 секунди"); }, 2000); сonsole.log("Кінець"); ``` Результат: Початок Кінець Дія через 2 секунди Це не та багатозадачність, як у деяких інших мовах програмування. Тут не використовуються додаткові потоки, а все працює завдяки механізму подій. Але про це детальніше дал Багатозадачність: процеси й потоки, у чому різниця Багатозадачність в операційній системі - це можливість запускати та керувати кількома задачами одночасно. Наприклад, працювати в браузері, слухати музику, завантажувати файл і паралельно редагувати код у Visual Studio. На практиці процесор дуже швидко перемикається між усіма цими задачами, створюючи ілюзію одночасного виконання. Якщо процесор багатоядерний - деякі задачі справді можуть виконуватись паралельно. Багатозадачність тісно пов'язана з двома важливими поняттями - процесами та потоками. Процес (process) - це окремий екземпляр програми у пам'яті, який має власні ресурси: виділену область оперативної пам'яті, дескриптори файлів, змінні оточення тощо.  Потік (thread) - це одиниця виконання всередині процесу. Потоки одного процесу працюють незалежно, але мають спільний доступ до пам'яті та ресурсів процесу. Процеси дозволяють запускати різні програми одночасно - наприклад, Google Chrome, Visual Studio Code і т.д.  Потоки дають змогу виконувати кілька задач усередині однієї програми. Наприклад, у Visual Studio Code один потік відповідає за оновлення інтерфейсу, інший перевіряє помилки в коді, ще один формує підказки під час написання. Це, звісно, спрощений приклад - у реальності VS Code використовує ще й окремі процеси для розширень і мовних серверів. Операційна система керує як процесами, так і потоками. Вона розподіляє процесорний час між ними, ставить у чергу, може призупиняти виконання або відновлювати його за потреби. Давайте трохи адаптуємо наш приклад з кухарем із попереднього посту. Уявімо, що процес - це ресторан, а потік - це кухар. Ресторан має все необхідне для приготування їжі: кухонне приладдя, продукти, рецепти (це можна розглядати як пам’ять і доступ до інших ресурсів). Кухар читає рецепт і, використовуючи ресурси ресторану, готує страву - так само, як потік виконує інструкції нашої програми, використовуючи ресурси процесу. Якщо ресторан хоче готувати кілька страв одночасно, йому потрібно більше кухарів, які працюють паралельно на одній кухні. Аналогічно, якщо програма повинна виконувати кілька задач одночасно - завантажувати файли, обробляти введення, оновлювати інтерфейс - вона може використовувати кілька потоків. Коли ми створюємо програму і хочемо зробити її зручною для користувача, а також ефективною з точки зору використання ресурсів, які виділяє операційна система на процес, ми іноді починаємо використовувати потоки та прийоми багатопотокового програмування. Це велика окрема тема, і ми її зараз чіпати не будемо. Одна з причин - у JavaScript немає прямого доступу до потоків. Уточнення. Якщо ви хочете використовувати JavaScript і все ж таки працювати з потоками - у вас є Web Workers:  https://developer.mozilla.org/en-US/docs/Web/API/Web_Workers_API/Using_web_workers  А якщо JavaScript виконується не в браузері (наприклад, у Node.js), тоді можна використовувати модуль `worker_threads`. Але варто розуміти, що це не частина стандарту мови, а можливість середовища виконання. Додаткові корисні ресурси по цій темі: https://www.youtube.com/@CoreDumpped - канал з короткими відео про те як працює комп'ютер. Modern Operating System by Andrew Tanenbaum - принцип побудови та роботи операційних систем (може бути викликом для новачка, але нормально як для технічної книжки) Особливості багатозадачності в JavaScript JavaScript працює в одному потоці - це означає, що в будь-який момент часу виконується лише один фрагмент коду. Увесь код, який ми пишемо, виконується у call stack: це структура, в яку потрапляють усі функції, що викликаються. Якщо одна з функцій виконується довго (наприклад, важке обчислення), усі інші задачі - включно з обробкою кліків, рендерингом чи відповідями від сервера - будуть чекати, поки call stack не звільниться. Щоб не блокувати цей єдиний потік, браузер надає асинхронні API (setTimeout, fetch, Web API). Коли ми викликаємо, скажімо, fetch(), у стек додається лише короткий виклик цієї функції. Власне мережевий запит виконується в окремому потоці, який створює браузер. Тобто, один потік виконує задачі які є у call stack, а інший потік чекає поки відповідь поверне сервер. Але асинхронна операція колись завершиться і треба механізм який віддасть нашому головному потоку результат роботи іншого потоку. Коли це стається колбек або проміс‑резолвер не додається одразу у call stack. Спершу він потрапляє до черги подій (task queue). За роботою черги стежить event loop. Його правило просте - поки стек порожній дістати першу задачу із черги і покласти у стек. Так ми досягаємо псевдобагатозадачності: основний потік виконує короткі шматки коду послідовно, а довгі операції «живуть» поза стеком. Коли довгі операції завершуються вони формують чергу задач, які треба виконати а event loop ці задачі закидає до стеку, коли call stack стає порожнім. Це максимально спрощене пояснення, і без візуалізації може здатися складним. Якщо хочете краще зрозуміти, дуже раджу подивитись відео Jake Archibald — "In The Loop" на YouTube (англійською).  https://www.youtube.com/watch?v=8aGhZQkoFbQ Або приходьте на мій курс JavaScript Поглиблений, де ми розбираємо це на практиці.  Також корисна стаття на MDN, де ці процеси описані докладніше. Асинхронні операції на практиці: HTTP-запит як найпоширеніший кейс Один з прикладів асинхронної операції - це запит на сервер через HTTP-протокол. Якщо організовувати запит через JavaScript у браузері без використання React, Angular або Vue.js, то це можна зробити за допомогою: fetch XMLHttpRequest Спеціалізована бібліотека, наприклад, Axios Ось так буде виглядати простий код написаний на fetch ```js fetch('https://jsonplaceholder.typicode.com/users')   .then(res => res.json())   .then(data => console.log(data)); ``` А так на axios (axios одразу повертає розпарсений JSON як response.data, на відміну від fetch, де потрібно викликати .json() вручну) ```js axios.get('https://jsonplaceholder.typicode.com/users')   .then(response => console.log(response.data)); ``` Якщо розглянути саме fetch то ось що відбулося під капотом: fetch створює HTTP запит вказавши HTTP метод, заголовки, тіло тощо Цей запит передається у вбудовану систему Web API - окрему від JavaScript середу, яка працює в іншому потоці. JavaScript не чекає відповіді - основний потік продовжує виконувати інший код fetch повертає Promise - об'єкт, що представляє асинхронну операцію, результат якої з’явиться пізніше Коли відповідь від сервера приходить, Web API кладе callback в чергу. Event Loop перевіряє, чи call stack порожній, і виконує цю мікрозадачу. Така поведінка дозволяє браузеру одночасно виконувати інші задачі, не чекаючи завершення запиту. Про використання асинхронного коду в JavaScript є [безкоштовний урок на YouTube](https://www.youtube.com/watch?v=cvR1EQ1R0EQ) Також більше про синтаксис Promise можна дізнатися в уроці [Асинхронний код. Promise](https://itvdn.com/ua/video/javascript-essential-ua/js-promise-ua) на ITVDN, а детальніше про варіанти організації такого коду буде написано далі. Підходи до написання асинхронного коду Складність роботи з асинхронним кодом полягає в тому, що обробка результату операції відбувається не відразу, а через певний час після її запуску. Ми ініціюємо асинхронну операцію й можемо виконувати інші завдання, але все одно маємо якось дізнатися про її завершення та обробити результат. Проблема в тому, що в цей момент програма вже виконує інші дії. Тому для обробки асинхронних операцій використовується push-модель взаємодії: отримувача даних (наш код) викликає провайдер даних - окремий механізм, який керує асинхронною операцією. По суті, розробнику потрібно відреагувати на подію завершення асинхронної операції. Для цього існує кілька підходів: callback-функція Promise async/await (синтаксичний цукор над Promise) Observables Використання функцій зворотнього виклику (callback) Почнемо з callback-функцій. Це найпростіший підхід, але він може призвести до заплутаного коду, особливо коли одна асинхронна операція запускає іншу, і так утворюється ланцюг. Уявімо, що маємо функцію downloadImage(url, callback), яка завантажує зображення асинхронно, не блокуючи основний потік. Перший параметр - це адреса зображення, яке потрібно завантажити, а другий - функція, яку буде викликано після завершення завантаження. При цьому саме зображення буде передане як параметр у callback. Приклад використання: ```js downloadImage(url1, image => document.body.append(image)) ``` На перший погляд усе просто. Але якщо потрібно завантажити кілька зображень послідовно, код стає менш зрозумілим: ```js downloadImage(url1, image => {             document.body.append(image);             downloadImage(url2, image => {                         document.body.append(image);                         downloadImage(url3, image => {                                     document.body.append(image);                         })             }) }); ``` Така вкладена структура швидко ускладнюється, особливо якщо замість одного рядка з DOM-змінами з’являється додаткова логіка. Подібний стиль називають "Pyramid of Doom", і його краще уникати. Один зі способів спростити обробку асинхронних викликів - це використання Promise. Використання Promise Promise - це об’єкт, який представляє асинхронну операцію. У перекладі з англійської promise означає «обіцянка». Можна уявити, що це обіцянка від браузера надати в майбутньому або результат операції, або помилку, пов’язану з її виконанням. Приклад використання: перепишемо попередню функцію downloadImage, щоб вона повертала Promise. ```js let promise = downloadImage(url1); promise.then(image => document.body.append(image)); ``` Тут ми все одно використовуємо callback-функцію, але передаємо її вже в метод .then() об’єкта promise. Це важливий момент: тепер асинхронна операція має об’єктне представлення, яке можна передавати як параметр у різні частини коду; можна будувати ланцюжки промісів, позбуваючись вкладеності, яка виникала з callback. Приклад: ```js downloadImage(url1)                          // отримуємо проміс .then(image => {                             // вказуємо що робити коли promise перейде в стан resolved             document.body.append(image);             return downloadImage(url2);                // виконуємо метод, який повертає promise }) .then(image => {                             // результат роботи попереднього промісу передається як значення             document.body.append(image);             return downloadImage(url3); }) .then(image => {             document.body.append(image); }); ``` Тепер код виглядає лінійним і набагато зручнішим для супроводу. У прикладах вище ми не розглядали, як саме створюється Promise, адже важливо зрозуміти мотивацію використання цих об’єктів. Тим більше, що більшість API браузера вже повертають готові проміси. Наприклад: ```js fetch('<https://jsonplaceholder.typicode.com/users>')   .then(res => res.json()); ``` Якщо хочете детальніше розібратися зі створенням Promise вручну - перегляньте документацію на MDN або мій відео урок на ITVDN. Async/await Супроводжувати синхронний код завжди простіше, ніж асинхронний. У 2012 році в мові C# з’явився синтаксичний цукор, який значно спростив роботу з асинхронними операціями: замість вкладених callback можна було використовувати послідовний синтаксис з новими ключовими словами async та await. Згодом цю концепцію перейняли й інші мови програмування, зокрема Python та JavaScript. В JavaScript підтримку async/await додали у 2017 році. Призначення ключових слів async - додається до функції та вказує, що вона завжди повертає Promise. await - використовується перед об’єктом-промісом, щоб "дочекатися" результату перед виконанням наступних рядків коду. Перепишемо попередній приклад із завантаженням зображень, використовуючи async/await: ```js let image = null;   image = await downloadImage(url1); document.body.append(image);   image = await downloadImage(url2); document.body.append(image);   image = await downloadImage(url3); document.body.append(image); ``` Тепер код виглядає як звичайний синхронний: немає вкладених callback, усе читається рядок за рядком. Можна подумати, що await "зупиняє" виконання, очікуючи на результат промісу. Насправді ж код не блокує основний потік - під капотом він перетворюється на машину станів, де кожен стан описує дію до або після await. Ще одна перевага async/await - знайомий синтаксис для обробки помилок: ```js try {             let image = null;               image = await downloadImage(url1);             document.body.append(image);               image = await downloadImage(url2);             document.body.append(image);               image = await downloadImage(url3);             document.body.append(image); } catch (ex) { // обробка помилки } ``` У результаті асинхронний код виглядає так само зрозуміло, як і синхронний, що значно спрощує його супровід. Observable Observable - це ще один підхід до організації асинхронного коду. Назва походить від однойменного патерна проєктування (Observer pattern), який описує створення об’єктів, за якими можна «спостерігати», отримуючи від них сповіщення. Тобто це реалізація подієвої моделі за допомогою ООП. У сучасному JavaScript ця ідея пішла далі й стала основою реактивного програмування та бібліотеки RxJS. Якщо Promise представляє одне майбутнє значення (успішне або помилкове), то Observable - це потік значень, які можуть з’являтися з часом. Можна уявити: Promise - це одна посилка, яку ви отримаєте в майбутньому; Observable - це підписка на журнал, нові випуски якого надходитимуть регулярно. Щоб створити Observable, використовують конструктор або готові оператори RxJS. Наприклад, функція downloadImages(urls) може завантажувати кілька картинок і відправляти їх «у потік» по мірі завантаження: ```js import { Observable } from 'rxjs';   function downloadImages(urls) {   return new Observable(subscriber => {     urls.forEach(url => {       downloadImage(url, image => {         subscriber.next(image); // надсилаємо картинку у потік       });     });     subscriber.complete(); // повідомляємо, що потік завершено   }); } ``` Щоб використати Observable, на нього треба підписатися за допомогою subscribe: ```js downloadImages([url1, url2, url3])   .subscribe({     next: image => document.body.append(image), // що робити з новим значенням     error: err => console.error(err),           // обробка помилок   }); ``` Переваги Observable працюють із потоком даних, а не з одним результатом; підтримують підключення операторів трансформації (фільтрація, мапінг, комбінування потоків); можна легко скасувати виконання (відписатися від потоку). Нижче приклад обробки даних через оператори. В RxJS оператори підключаються через метод pipe: ```js import { filter, map } from 'rxjs/operators'; downloadImages([url1, url2, url3])   .pipe(     filter(image => image.width > 100), // пропускаємо лише великі картинки     map(image => {       image.classList.add('highlight');       return image;     })   )   .subscribe({     next: image => document.body.append(image),     error: err => console.error(err),     complete: () => console.log('Готово')   }); ``` Таким чином, як і у випадку з Promise, можна будувати ланцюжки обробки. Але Observable значно гнучкіші: вони дозволяють працювати не лише з одним значенням, а з динамічною послідовністю даних у часі. Для глибшого занурення рекомендую офіційний гайд Observable на RxJS.dev. та відео уроки Observables. Частина 1, та Observables. Частина 2[1]  Практичні поради по работі за асинхроним кодом Не змішуйте підходи без потреби Якщо почали писати з async/await, не вставляйте всередину .then() без особливої причини. Це ускладнює читання. Обробляйте помилки Використовуйте try/catch для async/await або .catch() для Promise. У випадку Observable завжди додавайте обробку error у subscribe(). Скасовуйте непотрібні операції Для Observable використовуйте unsubscribe(), коли потік більше не потрібен. Для fetch можна застосувати AbortController, щоб зменшити навантаження й уникнути витоків пам’яті. Уникайте "Pyramid of Doom" Замість вкладених callback застосовуйте Promise, async/await або Observable. Використовуйте паралельне виконання Якщо операції незалежні, запускайте їх одночасно через Promise.all(). Ізолюйте логіку обробки Розділяйте завантаження даних та маніпуляції з DOM. Це спростить тестування й повторне використання коду. Логуйте стан і помилки Під час розробки виводьте у консоль ключові події асинхронних операцій, щоб відстежувати їх послідовність. Пам’ятайте про event loop Розуміння різниці між мікрозадачами й макрозадачами допоможе прогнозувати порядок виконання коду. Перевіряйте сумісність середовища Деякі API можуть бути відсутні у певних середовищах (наприклад, fetch у Node.js доступний лише починаючи з версії 18).
300+ запитань щодо JavaScript на співбесіді

Автор: Влад Сверчков

Дорогие друзья! Предлагаем вашему вниманию перевод статьи, опубликованной на DOU.ua 21 декабря 2020 года. Оригинальная версия на украинском языке доступна по ссылке. На этот раз предлагаем ознакомиться с актуальными вопросами, которые задают на технических интервью по JavaScript. Естественно, мы говорим о широком спектре специализаций, поэтому выбирайте свое направление и готовьтесь.   Junior Общие: 1. Какие методы HTTP-запросов вы знаете? 2. Какие версии HTTP-протокола вам известны? 3. Какие знаете коды ответа (состояния) HTTP? 4. Что такое Cross-Origin Resource Sharing? Как устранить проблемы с CORS? 5. Что такое cookie? 6. Какой максимальный размер cookie? 7. Что означает директива use strict? 8. Чем JS отличается при работе на front-end и back-end? 9. Что такое статическая и динамическая типизации? 10. Как клиент взаимодействует с сервером? 11. Что такое REST? 12. Объяснить понятие мутабельность/иммутабельность? Какие типы являются мутабельными и наоборот? 13. Как искать ошибки в коде? Используете ли вы дебаггер? 14. Каких известных людей из мира JS знаете?   JS Core 15. Какие существуют типы данных в JS? 16. Как проверить, является ли объект массивом? 17. Как проверить, является ли число конечным? 18. Как проверить, что переменная равна NaN? 19. Чем отличается поведение isNaN() и Number.isNaN()? 20. Сравните ключевые слова var, let, const. 21. Что такое область видимости? 22. Что такое деструктуризация? 23. Для чего предназначены методы setTimeout и setInterval? 24. Сравните подходы работы с асинхронным кодом: сallbacks vs promises vs async / await. 25. Можно ли записывать новые свойства / функции в прототипы стандартных классов (Array, Object и т. д.)? Почему нет? В каких случаях это делать можно? Как обезопасить себя, если нужно расширить прототип? 26. Назовите методы массивов, какие помните, и скажите, для чего они нужны. 27. Какие методы перебора массива знаете? В чем их отличие? 28. Как работают операторы присваивания / сравнения / строчные / арифметические / битовые и т. д.? 29. Опишите назначение и принципы работы с коллекциями Map и Set. 30. Что означает глубокая (deep) и поверхностная (shallow) копия объекта? Как сделать каждую из них? Ответы на некоторые из этих вопросов вы можете найти в видео курсе JavaScript Стартовый (урок 3, урок 5, урок 10), JavaScript Базовый (урок 3, урок 13, урок 19), ECMAScript 6 (урок 6).   Функции: 31. Какая разница между декларацией функции (function declaration) и функциональным выражением (function expression)? 32. Что такое анонимная функция? 33. Расскажите о стрелочных функциях (arrow function). В чем заключаются отличия стрелочных функций от обычных? 34. Что такое и для чего используют IIFE (Immediately Invoked Function Expression)? 35. Что такое hoisting, как он работает для переменных и функций? 36. Что такое замыкание (closure) и какие сценарии его использования? 37. Как вы понимаете замыкания? Что будет выведено в консоли в этом случае? var f = function() {   console.log(1); } var execute = function(f) {   setTimeout(f, 1000); } execute(f); // что выведет в консоль и почему f = function() {   console.log(2); } 38. Что такое рекурсия? 39. Что означает ключевое слово this? 40. Что такое потеря контекста, когда происходит и как ее предотвратить? 41. Методы функций bind / call / apply - зачем и в чем разница? Ответы на некоторые из этих вопросов вы можете найти в видео курсе JavaScript Стартовый (урок 11, урок 12) и JavaScript Базовый (урок 13, урок 14).   Front-end 42. Что такое DOM? 43. Сравните атрибуты подключения скрипта async и defer в HTML-документе. 44. Какая разница между свойствами HTML-элементов innerHTML и innerText? 45. Опишите процесс всплытия (bubbling) событий в DOM. 46. Как остановить всплытие (bubbling) события? 47. Как остановить дефолтную обработку события? 48. Чему равен this в обработчике событий (event handler)? 49. Что такое LocalStorage и SessionStorage? Какой максимальный размер LocalStorage? 50. Как получить высоту блока? Его положение относительно границ документа? 51. Что такое webpack? 52. Чем отличается dev-сборник от prod? Ответы на некоторые из этих вопросов вы можете найти в видео курсе JavaScript Базовый (урок 1, урок 6, урок 7, урок 8, урок 17) и ECMAScript 6 (урок 1).   Верстка 53. Что такое блочная модель CSS? 54. Какие способы центрирования блочного контента по горизонтали и вертикали знаете? 55. Какие подходы в верстке вам известны (float, flex, grid, etc.)? 56. Как сделать приложение responsive? 57. Какие есть принципы семантической верстки? 58. Зачем нужны префиксы для некоторых CSS-свойств (-webkit-, -moz- и т. д.)? 59. Как упростить написание кросс-браузерных стилей? 60. Практические задачи: прокомментировать и исправить пример плохого CSS или HTML. 61. Что такое CSS-препроцессоры? С какими работали? Что нового они приносят в стандартный CSS? Ответы на некоторые из этих вопросов вы можете найти в видео курсе HTML5 & CSS3 Стартовый (урок 5, урок 6), HTML5 & CSS3 Углубленный (урок 4). Курсы Верстка сайта на CSS Grid и Верстка сайта на FlexBox CSS дадут комплексные знания и практические навыки применения технологий FlexBox и Grid.   Angular 62. Перечислите основные компоненты фреймворка (модуль, роут, директива и т .п.). 63. В чем разница между компонентом и директивой? 64. Расскажите о жизненном цикле компонента. 65. Перечислите часто используемые хуки жизненного цикла компонента и расскажите, для чего они нужны? 66. В чем разница между конструктором и ngOnInit-хуком? 67. Как защитить роут от несанкционированного доступа? Какие механизмы предоставляет для этого фреймворк? 68. Что такое Lazy loading, как и для чего используется? 69. Какое назначение RouterOutlet? 70. Как компоненты могут взаимодействовать друг с другом? 71. Как создать two-way binding свойство для компонента? 72. Какие типы форм у фреймворка? В каких случаях и что лучше использовать? 73. Какие состояния у формы и как это можно применить? 74. Зачем нужны сервисы? Как с ними работать? 75. Что такое singleton-сервисы? Каково их назначение? Способ создания? 76. Какие есть способы объявления сервисов? 77. Для чего нужны модули? Сколько их должно быть в проекте? 78. Зачем нужны общие модули (shared)? 79. Какие преимущества типизации в TypeScript? 80. Какие возможности TypeScript можно использовать для типизации (здесь имеются в виду интерфейсы, типы, enum и т. д.)? 81. Какая разница между интерфейсом и классом? 82. В чем разница между интерфейсом и абстрактным классом? 83. Какая разница между интерфейсом и типом? 84. Что такое RxJS? Как он используется во фреймворке? Какие компоненты фреймворка тесно связаны с ним? 85. Чем отличаются Observable и Promise? 86. Для чего нужны Subjects? Какие типы Subjects существуют? 87. Как сделать несколько последовательных запросов к API с помощью HTTP-сервиса и RxJS? 88. Какая разница между switchMap, concatMap, mergeMap? 89. Как можно конфигурировать Angular-приложение? 90. Зачем нужны environment-файлы? Когда их лучше не использовать? 91. В чем разница между «умным» (smart) и «глупым» (dumb) компонентами? В каких случаях применяется каждый из них? 92. В чем разница между NgForm, FormGroup и FormControl и как их применяют для построения форм? 93. Зачем нужен и как работает async pipe? 94. Как следить за развитием фреймворка? Каких известных людей, связанных с Angular, знаете / читаете? Ответы на некоторые из этих вопросов вы можете найти в видео курсах Angular Базовый и Angular Углублённый.   React 95. Работали ли вы с классовыми компонентами? В чем их особенность? 96. Какие данные лучше хранить в состоянии компонента, а какие передавать через пропсы? Приведите пример. 97. Ознакомлены ли вы с хуками? В чем их преимущества? Приходилось ли делать свои и с какой целью? 98. Знакомы ли вы с фрагментами и порталами? Зачем они нужны? 99. Когда и для чего используют рефы? 100. Какие вы знаете методы жизненного цикла компонента? 101. В каком методе жизненного цикла компонента лучше делать запросы на сервер? Почему? 102. В каком методе жизненного цикла компонента лучше делать подписку и отписку от листенера? Почему? Зачем отписываться? 103. Был ли опыт работы с контекстом? Когда его стоит использовать? 104. В чем особенность PureComponent? 105. Работали ли вы с мемоизоваными селекторами (memoized selectors)? Для чего их используют и какой принцип работы? 106. В чем видите преимущества библиотеки React? 107. Почему библиотека React быстрая? Что такое Virtual DOM и Shadow DOM? 108. Зачем в списках ключи? Можно ли делать ключами индексы элементов массива? Когда это оправдано? 109. В чем основная идея Redux? 110. Работа со стилями в React. 111. React - это библиотека или фреймворк? Какая разница между этими двумя понятиями. 112. Можно ли использовать jQuery вместе с React? Почему да / нет? 113. Что такое codemod? 114. Приходилось ли вам настраивать проект React с нуля? С помощью каких инструментов вы это делали? 115. Перечислите все библиотеки, которые использовали в связке с React. 116. Что самое сложное вам приходилось реализовывать с помощью React? Ответы на некоторые из этих вопросов вы можете найти в видео курсах React Базовый и React Углублённый.   Back-end 117. Что такое REPL? 118. Что такое streams в Node.js? 119. Что такое middleware? 120. Для чего используют функцию setImmediate? 121. Зачем нужен app.param() в express? 122. Что такое token based authentication?   Базы данных 123. Напишите простой запрос для вычисления трех авторов, у которых больше всего книг. 124. Напишите запрос, который выбирает последние три комментарии для конкретного пользователя для двух таблиц: комментарии и пользователи. 125. Спроектируйте простую схему базы данных для библиотеки. 126. Для чего используют SQL-оператор HAVING? 127. Зачем используют SQL-оператор LEFT JOIN? 128. Чем отличается embed- от reference-связи в MongoDB? 129. В одном проекте программисты сохраняют данные в MongoDB-коллекции комментариев, используя такие типы данных (смотрите ниже). Что плохого в этом решении? id: ObjectID text: string author_id: string created_at: Date 130. В проекте понадобилось внести изменения в структуру таблиц, добавить несколько полей и индексы. Как программисты будут делать это на продакшене? Ответы на некоторые из этих вопросов вы можете найти в видео курсе SQL Базовый.   Инструменты 131. Каждый раз, когда вы делаете pull, почему-то случается конфликт в последней строке во всех файлах, которые вы редактировали. Что происходит? 132. Что делает команда git fetch? 133. Какой git hygiene подходы вы знаете? 134. Что такое CI / CD? Для чего это нужно?   Практические задания 135. Расскажите, какие есть способы копирования простого объекта типа obj = {a 1, b 2, c 3} 136. Напишите deep clone для объекта. 137. Назовите различные способы, как поменять местами значения двух переменных. 138. Менеджер попросил в задаче поменять статусы из «active, inactive» на «active, removed», но в коде фигурируют только цифры и непонятно, какой статус соответствует какой цифре. Как помочь будущим программистам не лезть в документацию по коду? Вопрос ставят на конкретном примере с кодом. 139. Необходимо сделать мини проект - список пользователей с формой создания / редактирования пользователя: Для хранения пользователей используйте Firebase (это бесплатно). Для стилизации используйте Bootstrap. Минимальный набор полей пользователя: имя; фамилия; электронная почта; телефон (в формате +380 (XX) XXX-XX-XX) дата рождения; будет плюсом - добавление аватара и возможность crop-картинки. Пользователи должны иметь возможность фильтрации и пагинацию. Проект должен содержать README-файл с шагами для запуска.   Middle Общие 1. Расскажите о пирамиде тестирования. 2. Какие типы автоматизированных тестов выпадала возможность писать? Какие библиотеки при этом использовали? Каким инструментам отдаете предпочтение и почему? 3. Что такое unit-тесты? Какое место в пирамиде тестирования занимают unit-тесты? 4. Что такое code coverage? Обязательно 100% покрытие тестами кода? 5. Как запретить браузеру отдавать кэш на HTTP-запрос? 6. Что такое XSS (Cross-Site Scripting)? 7. Расскажите о паттернах Observer, Pub / Sub. Какая между ними разница? Приведите примеры реализации этих паттернов в известных фреймворках / библиотеках / браузерных API. 8. С какой целью может быть использован event listener события fetch self.addEventListener ( 'fetch', event => {})? 9. Что такое Event loop и как он работает? Расскажите о микрозадачах и макрозадачах.   JS Core 10. Какие типы данных бывают в JavaScript? Какой будет результат выполнения кода? let firstObj = { name: 'Hello' }; let secondObj = firstObj; firstObj = { name: 'Bye' }; console.log(secondObj.name); 11. Что такое temporal dead zone? 12. Как работает boxing / unboxing в JavaScript? 13. В чем разница между оператором in и методом hasOwnProperty? 14. Опишите, с помощью чего в JS реализуются такие ООП-парадигмы, как инкапсуляция, полиморфизм, абстракция? 15. Что такое прототип? Как работает прототипное наследование в JS? Объясните работу кода. function Main () {} Main.prototype = { protected: true }; const obj = new Main(); Main.prototype = { protected: false }; console.log('Object protection: ', obj.protected); 16. Какая разница между композицией и наследованием? 17. Почему не стоит использовать конструкторы типа new String? 18. Расскажите о базовом устройстве и механизме работы Event loop. 19. Что такое записи (records) и кортежи (tuples)? Чем они отличаются от обычных объектов? 20. Какие различия в поведении ES5 функции-конструктора и ES2015 класса? 21. Как реализовать паттерн «Модуль»? 22. Почему typeof null возвращает object? 23. Что такое приведение (преобразование) типов в JS? 24. Что такое явное и неявное приведение (преобразование) типов данных в JS? Как происходит преобразование типов в следующих примерах: {}+[]+{}+[1] !!"false" == !!"true" ['x'] == 'x' 25. Что такое Garbage Collector? 26. Опишите основные принципы работы «сборщика мусора» в JS-движках (engines). 27. Опишите назначение и принципы работы с коллекциями WeakMap и WeakSet? Чем они отличаются от коллекций Map и Set соответственно? 28. Чем отличается Observable от Promise? 29. Что такое Promise? Назовите порядок выполнения then и catch в цепочке. Promise.resolve(10)   .then(e => console.log(e)) // ??   .then(e => Promise.resolve(e))   .then(console.log) // ??   .then(e => {     if (!e) {       throw 'Error caught';     }   })   .catch(e => {     console.log(e); // ??     return new Error('New error');   })   .then(e => {     console.log(e.message); // ??   })   .catch(e => {     console.log(e.message); // ??   }); 30. Расскажите о последовательном и параллельном выполнении асинхронных функций. В чем разница между Promise.all() и Promise.allSettled()? 31. Что такое дескрипторы свойств объектов? Расскажите об их практическом применение. 32. Назовите несколько способов создания постоянного (неизменного) объекта в JavaScript. 33. Как создать свойство у объекта, которое нельзя будет изменить? 34. Зачем нужен конструктор Proxy? Приведите пример использования. 35. Что такое ArrayBuffer? В чем разница между Uint32Array и Float32Array? Каков результат выполнения кода? const uint32Array = new Uint32Array(); Array.isArray(uint32Array); 36. Каким будет результат сравнения? const url = “HTTPs://xyz.com/path<to>page.html”; encodeURI(url) == encodeURIComponent(url); 37. Расскажите о генераторах и итераторах. 38. Объясните, что делает приведенный ниже код: function * fn(num) {   for (let i = 0; i < num; i += 1) {     yield console.log(i);   } } const loop = fn(5); loop.next(); loop.next(); 39. Расскажите о типе данных Symbol и его практическом применении. Как перевести число с 10-разрядной системы в 16 (2,8) разрядную систему счисления? Ответы на некоторые из этих вопросов вы можете найти в видео курсе JavaScript Стартовый (урок 3, урок 4, урок 13, урок 14) и JavaScript Базовый (урок 18, урок 19),  ECMAScript 6 (урок 6).   Функции 40. Объясните, что означает currying. Приведите пример использования на практике. 41. Приведите пример функции с мемоизацией. Когда следует применять эту технику? 42. Что такое чейнинг функций? Напишите пример с использованием этого подхода. 43. В чем разница между function и arrow function? Каким будет результат выполнения кода? const pluckDeep = key => obj => key.split('.').reduce((accum, key) => accum[key], obj) const compose = (...fns) => res => fns.reduce((accum, next) => next(accum), res) const unfold = (f, seed) => {   const go = (f, seed, acc) => {     const res = f(seed)     return res ? go(f, res[1], acc.concat([res[0]])) : acc   }   return go(f, seed, []) }   Front-end 44. В чем принципиальная разница между событиями mouseleave и mouseout? 45. В каком порядке обрабатываются пользовательские события в DOM (click, mouseover и т .д.)? FIFO или LIFO? 46. Что такое Event bubbling и Event capturing? 47. Сравните методы объекта event stopPropagation и stopImmediateProparation. 48. Какие есть подходы оптимизации производительности веб-страницы? 49. Как реализован механизм same-origin policy в браузере? На какие браузерные API он распространяется? 50. Назовите способы хранения данных в браузере. Сравните их. 51. Web worker`ы. Опишите особенности передачи данных между worker`амы и основным потоком, между разделенными worker`амы. 51. Что такое Transferable-объекты? 52. Расскажите о способах оптимизации выполнения ресурсоемких операций JS для улучшения производительности рендеринга контента на странице. 53. Почему ResizeObserver вызывает события изменения размера до воспроизведения элемента, а не после? 54. Расскажите, как вы понимаете Web Accessibility? 55. Опишите алгоритм создания функционала, который обеспечивает чтение содержимого .txt файла при перетаскивании его из файловой системы в окно браузера. 56. Что такое Virtual DOM? Ответы на некоторые из этих вопросов вы можете найти в видео курсе HTML5 & CSS3 Углубленный (урок 3), JavaScript Базовый (урок 1, урок 2, урок 3).   Верстка 57. Объясните разницу между единицами измерения px, em, rem. 58. Для чего нужны CSS-переменные? Приведите несколько примеров использования. 59. Что произойдет при добавлении следующего селектора? * {Box-sizing: border-box; } 60. Как адаптировать страницу для печати? 61. Опишите особенности кастомизации стилей стандартных элементов форм. 62. Что такое progressive рендеринг? Какие подходы используются? 63. Назовите несколько способов реализации lazy-loading медиаресурсов на странице. 64. Назовите популярные шаблонизаторы для фронтенд-разработки. Опишите особенности их использования. 65. Назовите популярные CSS-методологии и их различия. 66. Как работает CSS Grid? 67. Какие форматы изображений поддерживают анимацию? 68. Как отследить прогресс / окончание CSS @keyframes анимаций или плавных переходов, реализованных с помощью transition, в JS? 69. Какие CSS-свойства могут быть обработаны непосредственно через GPU? Что такое композитные слои и почему большое их количество может привести к аварийному завершению работы браузера на мобильных устройствах? 70. Как переиспользовать Инлайн SVG-элементы на странице? 71. Опишите способы оптимизации SVG-файлов. 72. Как реализовать иконочный шрифт из определенного набора SVG-файлов? 73. Что такое ложное жирное или ложное курсивное (Faux) начертание шрифтов? 74. Что такое #shadow-root в инспекторе HTML-страницы? 75. Зачем нужны Custom Elements? 76. Почему удаление лишних символов пробелов / символов переноса в HTML не отражается на конечной производительности загрузки страницы? 77. Что такое контекст отображения canvas? Какие существуют типы контекста для рендеринга двумерной и трехмерной графики? Ответы на некоторые из этих вопросов вы можете найти в видео курсе HTML5 & CSS3 Углубленный.   Angular 78. Как работает Dependency injection? Зачем это нужно? Расскажите об использовании кастомных инжекторов. 79. Что такое zone.js? Для чего Angular использует зоны? С какой целью можно использовать NgZone-сервис? 80. Как работает Change detection? Как можно оптимизировать компонент с помощью схем Change detection? Какие еще есть приемы для оптимизации рендеринга (связанные с Change detection)? 81. Как выполнить конфигурацию HTTP-сервиса? Зачем она нужна? Обработка HTTP-ошибок? 82. Какие есть подходы к организации работы с данными? 83. Как подготовить сборник к деплою? 84. Что такое NgRx? Когда стоит использовать? 85. В каких случаях лучше использовать Renderer-сервис вместо нативных методов? И наоборот? 86. Как работают и для чего нужны резолверы? Как получить данные, загруженные резолверами? 87. Как работают и зачем нужны динамические компоненты? Приведите примеры их целесообразного использования. 88. Какая разница между @ViewChild и @ContentChild? 89. Что делает код и как иначе можно связать класс компонента с переменной? @HostBinding ( 'class.valid') isValid; 90. Как можно кэшировать данные, используя сервисы или RxJS? 91. Что такое асинхронная валидация форм? Когда применяется и как реализуется? 92. Зачем нужна forRoot-функция модуля? 93. Какая разница между декларированием и экспортом компонента из модуля? 94. Почему плохо «провайдить» сервис с shared-модуля в lazy-loaded модуль? (Вопрос о scope модулей.) 95. Что такое :: ng-deep и для чего используется? 96. Какие тесты можно запустить для Angular-программы? Какие инструменты используют для тестирования Angular-программы? 97. Как протестировать API-сервис? Ответы на некоторые из этих вопросов вы можете найти в видео курсах Angular Базовый и Angular Углублённый.   React 98. Что такое JSX? Что лежит в его основе? 99. Как работает алгоритм Virtual DOM? 100. Для чего нужно свойство key во время рендеринга списков? 101. В чем разница между функциональными и классовыми компонентами? 102. Зачем и когда нужно передавать props в super() при использовании классовых компонентов? 103. Почему нужно использовать setState() для обновления внутреннего состояния компонента? 104. В чем заключается принцип «подъема состояния»? 105. Какие библиотеки менеджмента состояния React-приложения вы знаете? Зачем они нужны? 106. Когда следует использовать Redux? Какие есть альтернативы? 107. Redux vs Mobx? 108. Расскажите о базовом принципе работы React Hooks. 109. В чем разница между createRef и useRef? 110. Когда следует использовать React refs? Когда не стоит? 111. Какие недостатки библиотеки React видите? 112. Какие паттерны используете вместе с React? 113. Как относитесь к типизации вместе с React? 114. Как построить хорошую архитектуру React-проекта? 115. Оптимизация React-приложений? Как измерить производительность программы? 116. Можно ли приложение на React встроить в другое приложение на React? Ответы на некоторые из этих вопросов вы можете найти в видео курсах React Базовый и React Углублённый.   Back-end 117. Почему Node.js однопоточный, а не многопоточный? 118. Что такое event driven development? 119. Сравните fork() и spawn() методы. 120. Расскажите о Node.js фреймворках, которые использовали. Какая между ними разница? 121. Опишите словам код ендпоинта, который должен сохранить с клиента файл размером 4 гигабайта и положить его на S3 или другой CDN. 122. Что такое микросервисы, зачем их используют? 123. В каких случаях вы бы выбрали монолит, а в каких - микросервисы? 124. Как понять, что приложение в определенный момент работает исправно? 125. Как понять, что приложение за последние три дня работал исправно? 126. Как происходит проверка правильности пароля при использовании bcrypt? 127. Что такое JWT? 128. Джуниор прислал код на ревью. Что здесь не так? Как исправить? router.post ( '/ users', async (req, res, next) => {   const user = await db.createUser (req);   if (user) {     return res.json (users);   }   res.json ({error: "can not create user"}) })   Базы данных 129. Что такое Redis и для чего его используют? 130. Какие базы данных использовали? Какая разница между SQL и NoSQL? 131. Для двух таблиц - комментарии и пользователи - напишите запрос, который выбирает последние три комментария для каждого пользователя. 132. Я как заказчик прошу выбрать вас базу данных для нового проекта. Ваши действия?   Инструменты и другое 133. Для чего нужен package-lock.json? 134. В чем разница между npm install и npm ci? 135. Для чего нужны бандлеры? 136. Расскажите о модульном подключении скриптов. Приведите пример использования загрузчиков / бандлеров модулей. 137. Чем различаются git merge и git rebase? 138. Что такое staging area в git? 139. Опишите процесс code review. Назовите основные правила, способы разрешения конфликтов и споров во время его проведения.   Практические задания 140. Напишите функцию Sleep (ms), которая останавливает выполнение async-функции на заданный промежуток времени. 141. Реализуйте один из методов массива (например, splice). 142. Напишите функцию с RegExp для нахождения всех HTML-ссылок в строке. 143. Реализуйте функцию, которая исполнит callback для всех элементов определенной ветви DOM-дерева. 144. Реализуйте таблицу с виртуальным скролом. 145. Реализуйте функцию преобразования URL query строки в JSON.   const inData = "user.name.firstname=Bob&user.name.lastname=Smith&user.favoritecolor=Light%20Blue"; function queryObjectify(arg) { // ?? } queryObjectify(inData) /* Результатом виконання для вхідного рядка, повинен бути наступний об’єкт {   'user': {     'name': {       'firstname': 'Bob',       'lastname': 'Smith'     },     'favoritecolor': 'Light Blue'   } }; */ 146. Реализуйте функцию нахождения пересечения двух массивов. const first = [1, 2, 3, 4]; const second = [3, 4, 5, 6]; function intersection (a, b) { // ?? } intersection(first, second) // -> [3, 4] 147. Реализуйте функцию / класс для генерации HTML. const HTMLConstruct = {}; HTMLConstruct.span('foo'); // -> <span>foo</span> HTMLConstruct.div.span('bar'); // -> <div><span>bar</span></div> HTMLConstruct.div.p( HTMLConstruct.span('bar'), HTMLConstruct.div.span('baz') ); // -> <div><p><span>bar</span><span>baz</span></p></div> 148. Если есть проект с ограниченными сроками и некритичной производительностью, чем будете руководствоваться при выборе библиотек, подходов? Или все же будете обращать внимание на производительность? Или наоборот: сроки нелимитированные, производительность важна. Ваши действия?   Senior Общие 1. Расскажите о функциональном программировании. 2. Что такое TDD (Test Driven Development) / BDD (Behaver Driven Development)? 3. Расскажите подробно о работе HTTPS. 4. Какой стек технологий можно выбрать для реализации клона какого-нибудь известного проекту и почему? 5. Имеется проект на старых технологиях, необходимо в него вносить изменения. Как это сделать лучше всего? 6. Если у кандидата есть опыт работы с несколькими фреймворками: какой будете использовать для следующего проекта? Какие факторы будут влиять на выбор? 7. Что такое V8 Engine?   JS Core 8. Реализация паттерна Class Free OOP (HTTPs://observablehq.com/@bratter/class-free-oop). 9. Патерн async disposer (HTTPs://advancedweb.hu/what-is-the-async-disposer-pattern-in-javascript). 10. использование регулярных выражений. Когда приемлемо / неприемлемо? Как они работают? Как можно сделать читабельный код?   Front-end 11. Как браузер определяет, можем ли мы общаться между вкладками? 12. Что такое Content Security Policy? 13. Как избежать загрузки кэшированных файлов скриптов и стилей? 14. Что такое requestAnimationFrame? 15. Расскажите о микросервисной архитектуре Front-end App. 16. Что такое Shadow DOM? 17. Сравните nextElementSibling и nextSibling. 18. Какие знаете метрики веб-сайта?   Angular 19. Как проводится конфигурация NgZone-модуля? Когда это необходимо? 20. Что раздражает в фреймворке? Что бы вы изменили? 21. Если бы вы решали, что добавить в следующем релизе фреймворка, какая фича это была бы? 22. Писали ли вы кастомные декораторы? Если да, то зачем? 23. Сделать ревью кода и дать замечания по архитектуре. 24. Расскажите, как бы вы делали такие фичи. Опишите архитектуру фичи в приложении.   Back-end 25. Сравните Common.js с AMD Modules и ES6 Imports. 26. Какой фреймворк выбрали бы для бэкенда, почему? 27. Опишите своими словами, как работает OAuth v2. 28. Есть проект с источниками памяти, как их обнаружить, устранить и предотвратить это в будущем? 29. Есть проект с performance issues, как их обнаружить, устранить и предотвратить в будущем?   Базы данных 30. Какие альтернативные виды баз данных используете? 31. Что такое RDS и почему он иногда не подходит? 32. Что такое SQL Injections и как их избежать? 33. Почему для запросов в БД надо использовать плейсхолдеры? 34. Как спроектировать кластер на MongoDB? 35. Для чего используют MongoDB Aggregation framework? 36. Расскажите о GraphQL.   Инструменты 37. Можете ли вы описать суть методологии git flow в двух словах? 38. Что означает требование делать squash commits во время rebase? 39. Каково ваше мнение об альтернативных системы контроля версий (Version Control System)? 40. Какие конвенции знаете и используете для git? 41. Расскажите о своем опыте использования / внедрения CI / CD. 42. Необходимо настроить деплой проекту на несколько сред. Расскажите, как бы вы построили процесс? Какие инструменты использовали бы?   Практические задания 43. Реализуйте асинхронный метод filter для Array (должны работать await). 44. Реализуйте функцию reduce при помощи рекурсии. 45. Как можно было бы сделать toggle-компонент, как в iPhone, без использования JS?   Благодарим за помощь в подготовке статьи Вячеславу Колдовскому, Ивану Рыженку, Николаю Галкину, Александру Бурмистрову, Владу Балабашу, Андрею Шумаде, Ивану Кувацкому, Андрею Кладочному.
Введення в розробку програм під iOS. Частина 6

Автор: Volodymyr Bozhek

Здравствуйте,  дорогие читатели. В этом уроке мы: Установим менеджер установки пакетов “Brew”; Научимся пользоваться базой данных “Mongo DB” и изучим библиотеку “Mongoose”; Установим и сконфигурируем базу данных “Mongo DB”; Научимся запускать демон (службу) “Mongod” и подключаться к ней; Научимся просматривать документы базы данных “Mongo DB” с помощью приложения “Robomongo”; Создадим REST сервисы для товаров и пользователей с использованием “NodeJS”; Научимся тестировать REST сервисы с помощью расширения “Chrome” браузера “Postman”; Итак, вы скорее всего задались вопросом, почему же я выбрал именно такие технологии для написания REST сервисов. Почему не выбрал то, что уже давно у всех на слуху, такие технологии как Web API + Entity Framework. Ответ прост, я выбрал ровно те технологии, которые явно подходят платформе, на которой мы ведем разработку macOS. Да, можно было установить Xamarin Community Edition, в нем создать ASP.NET проект и сделать сервисы на Mono.NET. Еще был вариант сделать сервисы на виндовс платформе, развернутой на виртуальной машине в веб сервере IIS с сетевым адаптером мост. Это для того, чтобы веб сайт и сервисы были доступны на хостовой машине macOS в браузере. Возможно, в одном из моих будущих видео курсов по разработке iOS приложений я буду использовать именно платформу Windows и Visual Studio для разработки REST сервисов. Но в этом уроке  мы будем делать сервисы на NodeJS,  и вы сами убедитесь в том, насколько это просто и как мало кода надо для этого написать :) В качестве среды разработки я буду использовать WebStorm, он довольно хорошо себя оправдал как один из продуктов JetBrains. В этом уроке мы не будем работать с приложением “Warehouse” под iOS, поскольку мы должны приступить к изучению библиотеки “Alamofire”. А для нее необходимы REST сервисы, которых у нас сейчас нет. Показывать работу на всяких httpbin, parse и других сервисах тестирования я не буду, этого полно в сети интернет, вы и сами будете видеть это в поиске нужной вам информации. “Alamofire” -  это асинхронная сетевая библиотека для работы с REST сервисами. В большинстве туториалов  не рассказывается, как сделать REST сервисы и клиент к ним на разных платформах. Мне хотелось бы нарушить этот стереотип и предоставить вам учебные примеры по созданию полнофункционального REST сервиса и полнофункционального iOS клиента в составе приложения “Warehouse” к нему. Проще говоря, дать готовое решение  для быстрого понимания, что и как делается. Это то, чего обычно не хватает новичкам и то, чего обычно нигде не выкладывают. Приходится самостоятельно разбираться и искать нужную информацию. Откройте “WebStorm”. Выберите в меню “File -> New Project”. Выделите тип проекта “Node.js Express App”. В поле “Location”  укажите название проекта “Warehouse” и путь, по которому вы будете сохранять данный проект. В поле “Template”  выберите “EJS”. Нажмите кнопку “Create”. Вы увидите диалоговое окно с предложением, откуда взять библиотеку “NodeJS”. Оставьте пункт по умолчанию “Download from the Internet” и нажмите кнопку “Configure”. Будет загружен из сети интернет “NodeJS”, “Express” и другие библиотеки. Панель навигации будет выглядеть у вас так: Рассмотрим, что создал нам данный шаблон. В папке “bin”  хранится файл “www”, в нем находится настройка сервера “NodeJS”, с этого файла происходит запуск нашего сервера. В папке “node_modules”  находятся библиотеки, которые были автоматически загружены  шаблоном проекта NodeJS. В папке “public”  находятся ресурсы, используемые нашим сервером (картинки, CSS стили, скрипты). В папке “routes”  находятся скрипты, которые должны содержать маршрутизацию относительно текущего URL,  введенного пользователем. Например,  файл с именем “users.js”  вызывется, когда пользователь введет в браузере URL: “http://localhost:{port}/users”. Файл “index.js”  вызывется, когда пользователь введет URL: “http://localhost:{port}”. Если сравнивать с паттерном MVC (Model View Controller), то в папке “routes”  находятся “контроллеры”. В папке “views”  находятся представления. Расширение “*.ejs”  говорит о том, что содержимое данного файла будет транслироваться через библиотеку “Express JS”. Данная библиотека создана в помощь, чтобы можно было проще создавать маршрутизацию в NodeJS  и проще создавать пользовательский интерфейс. Внутри этих файлов “*.ejs”  содержится обычная “HTML” разметка. Файл маршрутизации и файл представления должны называться одинаково, если они будут связаны. В текущем проекте связаны только “index.js” и “index.ejs”. Маршрут “users.js”  не имеет явного представления, для него преставление  генерируется средствами “NodeJS”. Наша задача создать только REST сервисы, поэтому работу с “EJS”  мы не будем рассматривать в данном уроке. Рассмотрим содержимое файла “www” в папке “bin”. На 7 строке  через библиотеку “Require JS”  мы подключаем содержимое модуля “app.js”  в текущий исполняемый модуль “www” и запускаем это содержимое на исполнение. В файле “app.js”  содержатся настройки для работы нашего сервера. На 8 строке  мы задаем режим отладки для нашего сервера. На 9 строке  мы подключаем модуль “http”  для работы с HTTP запросами. На 15 строке  задается порт “3000”, на этом порту сервер будет принимать входящие запросы к нему. На 16 строке  устанавливает этот порт для свойства “port” сервера. На 22 строке  создается сервер на основе настроек экземпляра “app”. На 28 строке  сервер запускается и начинает прослушивание на порту “3000”. На 29 строке  привязывается функция “onError”, вызываемый при возникновении ошибок. На 30 строке  привязывается функция “onListening”, вызываемая  при начале прослушивания запросов сервером. Остальной код в данном модуле не представляет для нас интереса. Чтобы запустить приложение, выполните в меню “Run -> Run bin/www”, сервер запустится. Затем откройте браузер и введите URL: “http://localhost:3000”, вы увидите приветствие. Чтобы остановить сервер, выполните в меню “Run -> Stop”. Теперь необходимо установить базу данных “Mongo DB”. Подробнее об установке  вы можете прочитать тут. Проще всего устанавливать базу данных  через менеджер пакетов “Brew”. Давайте установим его. Перейдите в браузере по адресу, на сайте будет предложено скопировать строку “/usr/bin/ruby -e "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install)” в терминал. Откройте терминал и скопируйте в него строку установки “Brew”. После установки  вы увидите следующее: Теперь необходимо установить базу данных “Mongo DB”. Установка базы данных через “Brew”  проще тем, что “Brew”  сам заполняет нужные переменные среды окружения и вам не надо этого делать вручную  в случае с установкой базы данных вручную. Перейдите в “WebStorm”, откройте внутри него терминал. И введите команду “brew install mongodb && mongod”, данная команда скачает и установит “Mongo DB”, а также выполнит настройки по умолчанию для демона “mongod”. Выполните в терминале команду “sudo mkdir -p /data/db”. Введите свой пароль и нажмите кнопку “Enter”. Данная команда создаст папку “data” и в ней папку “db”, эта папка используется по умолчанию для добавления базы данных “Mongo DB”. Команда “sudo”  используется, чтобы выполнить остальную часть команды от имени администратора.       Теперь необходимо установить в наш проект библиотеку “Mongoose”  для осуществления возможности работы с базой данных их кода приложения сервера . Выполните в терминале команду “npm install mongoose”. Документацию по этой библиотеке можно почитать тут:  Данную команду мы выполняли через  встроенный в NodeJS  менеджер пакетов, так как устанавливаем данную библиотеку исключительно для нашего проекта, а не глобально. Откройте терминал, введите команду “mongod”. Данная команда запускает сервер базы данных “Mongo DB”, который принимает входящие подключения на порту “27017”. На картинке выше  вы можете в этом убедиться “waiting for connections on port 27017”. Демон запустили, теперь перейдите в “WebStorm”, в терминале внутри введите команду “mongod –version”, вы увидите версию базы данных “Mongo DB”, которая была установлена на ваш компьютер. Теперь необходим клиент с GUI  для работы с базой данных. Откройте браузер, перейдите по адресу, нажмите кнопку “Download”, скачайте и установите приложение. После установки приложения  откройте его. Нажмите на иконку: Откроется приложение “Robomongo”. Нажмите кнопку “Connect to local or remote Mongo DB instance”. Откроется диалоговое окно подключения к базе данных. Нажмите кнопку “Create”. Откройте диалоговое окно настройки подключения к базе данных. Заполните настройки, как показано на картинке выше и только на вкладке “Connection”, нажмите кнопку “Save”. После этого в окне “Mongo DB connections”  у вас появится добавленное подключение. Выделите его и нажмите кнопку “Connect”. Нажмите правой кнопкой в панели навигации по “Warehouse” и в контекстном меню выберите пункт “Create Database”. В поле “Database Name”  введите “warehouse”, нажмите кнопку “Create”. На картинке выше  видно, что база данных “warehouse”  была успешно добавлена. Супер :) Теперь надо добавить в проект модель и схему базы данных, они создаются с помощью библиотеки “Mongoose”. Перейдите в “WebStorm”. В панели навигации, нажмите правой кнопкой по “Warehouse”, в контекстном меню выберите “New -> Directory”. Создайте папку с именем “models”. Создайте в папке “models”  модули “Product.js” и “User.js”. А в папке “routes” создайте модули “auth.js”, “products.js”. Откройте модуль “Product.js”, заполните его, как показано на картинке ниже: На 1 строке  мы подключаем библиотеку “mongoose” и создаем ее экземпляр с именем “mongoose”. На 2 строке  мы правым операндом создаем схему таблицы товаров и присваиваем ее левому операнду с именем “ProductSchema”. Таблица товаров содержит поля: скрытое поле “_id”, типа “String”, в этом поле содержится уникальный идентификатор строки. скрытое поле “__v”, типа “Number”, в этом поле содержится версия API  по умолчанию 0. Версия API  нужна для того, чтобы можно было менять реализацию сервисов и иметь несколько версий  для обратной совместимости. поле “productImage”, типа “String”, тут будет храниться название картинок из нашего проекта под iOS, названия от “tool001” до “tool012”. поле “productName”, типа “String”, тут будет храниться название товара. поле “productDescription”, типа “String”, тут будет храниться описание товара. На 7 строке  мы правым операндом создаем экземпляр модели с именем “Product” и указываем создать экземпляр на основе схемы, описанной в экземпляре “ProductSchema”.      Библиотека “mongoose”  создает данный экземпляр и присваивает его левому операнду. Свойство “exports”  содержит экземпляры объектов, которые  будут доступны из модуля  при подключении его через библиотеку “Require JS”. Откройте модуль “User.js”, заполните его, как показано на картинке ниже: На 1 строке  мы подключаем библиотеку “mongoose” и создаем ее экземпляр с именем “mongoose”. На 2 строке  мы правым операндом создаем схему таблицы товаров и присваиваем ее левому операнду с именем “UserSchema”. Таблица пользователей содержит поля: скрытое поле “_id”, типа “String”, в этом поле содержится уникальный идентификатор строки. скрытое поле “__v”, типа “Number”, в этом поле содержится версия API  по умолчанию 0. Версия API  нужна для того, чтобы можно было менять реализацию сервисов и иметь несколько версий для обратной совместимости. поле “userName”, типа “String”, тут будет храниться логин пользователя. поле “userPwd”, типа “String”, тут будет храниться пароль пользователя. поле “userDesc”, типа “String”, тут будет храниться описание пользователя. На 7 строке  мы правым операндом создаем экземпляр модели с именем “User” и указываем создать экземпляр на основе схемы, описанной в экземпляре “UserSchema”. Затем присваем созданный экземпляр свойству “exports” данного модуля. Откройте модуль “auth.js”, заполните его в соответствии с содержимым ниже: На 1 строке  подключаем модуль “express” и создаем его экземпляр с именем “express”. На 2 строке  создаем экземпляр маршрутизатора запросов с именем “router”. На 3 строке  подключаем модуль “User.js” и создаем его экземпляр с именем “User”. На 4 строке  строим маршрут типа “http://localhost:3000/auth/”. На экземпляре “router”  вызываем функцию “post”. Вызов данной функции говорит, что созданный с помощью него маршрут будет доступен только через HTTP глагол POST.       Первым аргументом задаем маршрут, вторым аргументом задаем функцию обработчик, который будет вызван при переходе по данному маршруту с учетом заданного HTTP глагола. Т.е. когда пользователь отправит POST запрос по адресу. Функция обработчик принимает три аргумента. Первый аргумент “request”  содержит экземпляр запроса по данному маршруту.     Второй аргумент “response”  содержит экземпляр ответа по запросу по данному маршруту. В этот экземпляр можно добавлять любой ответ, который вы хотите, чтобы пользователь получил. Третий аргумент “next”  содержит текущий итератор в стеке запросов по маршрутизации. Данный экземпляр необходим, чтобы была возможность перейти на следующую итерацию в стеке маршрутизации. Сам по себе сервер NodeJS  работает асинхронно и никогда никого и ничего не ждет, это нужно учитывать при работе с ним. На 5 строке мы выводим сообщение на консоль. На 6 строке мы на экземпляре “User” вызываем функцию “find”, в которую единственным аргументом передаем специальную функцию обработчик, в которой будет содержаться результат данной операции. Функция “find”  получает все записи из таблицы “User” и присваивает их в JSON формате  второму аргументу функции обработчика с именем “data”. Для  примера, в “data” может содержаться такой JSON: “[{_id:'789787sdfsd78sdfsd7', __v:0, userName: “Test1”,...},{_id:'4444447sdfsd78sdfsd7', __v:0, userName: “Test2”,...}]”. Где квадратные скобки - это массив, а в фигурных скобках - это экземпляр JSON. В аргументе “err”  содержится экземпляр ошибки, если произошла ошибка при выполнении данной функции “find”. На 7 строке мы создаем экземпляр объекта с именем “res”, данный объект содержит свойство “Error”, которое инициализировано значением по умолчанию “Authorization Error”. Как вы уже поняли, это текст ошибки, который вернется на клиент в случае ошибки авторизации клиента. На 8 строке мы проверяем, если при получении данных через функцию “find” произошла ошибка и аргумент “err” не содержит значение “undefined”, мы обращаемся к экземпляру “next” и говорим серверу выполнить следующую итерацию маршрутизации и вернуть ошибку на клиент. На клиент средствами NodeJS  будет возвращена “Stack Trace” ошибки. На 9 строке, если ошибки не было, мы выполняем цикл foreach по массиву JSON объектов “data”. В функцию “forEach”  передается специальная функция, которая содержит три аргумента. Первый аргумент “item”  содержит текущий итерируемый экземпляр JSON. Второй аргумент “i” содержит позицию итератора и имеет тип Number. Третий аргумент “data”  содержит исходный массив объектов JSON, по которым проводится итерация. На 10 строке  мы задаем условие поиска пользователя по его имени и паролю в списке всех пользователей полученной функцией “find”. В условии сравниваются имя и пароль, которые пришли в POST запросе на сервер, с именем и паролем, полученным от функции “find”. На 12 строке, если пользователь был найден, мы присваиваем экземляру “res”  JSON объект найденного пользователя. На 15 строке  мы обращаемся к экземпляру ответа “response”, вызываем на нем функцию “json”, которая принимает данные и возвращает ответ на клиент в JSON формате. В аргумент данного метода мы передаем или экземпляр найденного пользователя, или экземпляр ошибки, что пользователь не найден и авторизация не удалась. На 18 строке  мы экспортируем из модуля экземпляр маршрутизации “router”. Откройте модуль “index.js”. Заполните его в соответствии с содержимым ниже: На 1 строке  подключаем модуль “express” и создаем его экземпляр с именем “express”. На 2 строке  создаем экземпляр маршрутизатора запросов с именем “router”. На 3 строке  строим маршрут типа “http://localhost:3000/index/”, доступный через HTTP глагол GET. На 4 строке, на экземпляр ответа вызываем функцию “render”. Данная функция возьмет представление, указанное в первом аргументе функции и вернет его на сторону клиента. Вторым аргументом задается словарь, в который можно добавить свойства со значениями, к которым можно будет обращаться со стороны представления. На 6 строке  мы экспортируем из модуля экземпляр маршрутизации “router”. Откройте модуль “index.ejs”. Заполните его в соответствии с содержимым ниже: Библиотека Express JS позволяет нам обращаться к свойствам, которые мы указали во втором аргументе метода “render”  путем добавления следующей конструкции “<%= Название_Свойства %>”. На этапе отрисовки страницы данные конструкции будут заменены на значение указанного свойства. Откройте модуль “products.js”, заполните его в соответствии с содержимым ниже: На 1 строке  подключаем модуль “express” и создаем его экземпляр с именем “express”. На 2 строке  создаем экземпляр маршрутизатора запросов с именем “router”. На 3 строке  подключаем модуль “Product.js” и создаем его экземпляр с именем “Product”. На 4 строке  строим маршрут типа “http://localhost:3000/products/”. Данный маршрут будет доступен по HTTP глаголу GET. На 5 строке  мы вызываем на экземпляре “Product”  функцию “find”. Внутри функции мы проверяем, были ли ошибки, и возвращаем в ответе JSON массив со списком всех товаров. На 10 строке  строим маршрут типа “http://localhost:3000/products/”. Данный маршрут будет доступен по HTTP глаголу POST. На 11 строке  мы вызываем на экземпляре “Product”  функцию “create”. Внутри функции мы проверяем, были ли ошибки, и возвращаем в ответе JSON объект успешно созданного товара. На 16 строке  строим маршрут типа “http://localhost:3000/products/{_id}” (например, “http://localhost:3000/products/789w66s2322kks4676s” ). Данный маршрут будет доступен по HTTP глаголу GET. На 17 строке  мы вызываем на экземпляре “Product”  функцию “findById”. Из названия функции ясно, что функция выполняет поиск по идентификатору товара. Для поиска используется скрытое поле “_id” в модели “Product”, объявленной в модуле “Product.js”. Внутри функции мы проверяем, были ли ошибки, и возвращаем в ответе JSON объект успешно найденного товара. На 23 строке  строим маршрут типа “http://localhost:3000/products/{_id}” (например, “http://localhost:3000/products/789w66s2322kks4676s” ). Данный маршрут будет доступен по HTTP глаголу PUT. На 24 строке  мы вызываем на экземпляре “Product”  функцию “findByIdAndUpdate”. Из названия функции ясно, что выполняется поиск товара по его идентификатору “_id”, затем, в случае если найден, обновляются все его поля, кроме скрытых полей. В ответе возвращается JSON объект успешно обновленного товара. На 30 строке  строим маршрут типа “http://localhost:3000/products/{_id}” (например, “http://localhost:3000/products/789w66s2322kks4676s” ). Данный маршрут будет доступен по HTTP глаголу DELETE. На 31 строке  мы вызываем на экземпляре “Product”  функцию “findByIdAndRemove”. Из названия функции ясно, что выполняется поиск товара по его идентификатору “_id”, затем, в случае если найден, товар удаляется. В ответе возвращается удаленный JSON объект. Откройте модуль “users.js”, заполните его в соответствии с содержимым ниже: В модуле “users.js”  я не вижу смысла расписывать подробно, что происходит, поскольку тут все то же самое, что и происходило в модуле “products.js”, рассмотренном выше. Разница только в том, что мы используем другую модель данных “User”. Опишу только доступные маршруты. На 4 строке  строим маршрут типа “http://localhost:3000/users/”. Данный маршрут будет доступен по HTTP глаголу GET. На 10 строке  строим маршрут типа “http://localhost:3000/users/”. Данный маршрут будет доступен по HTTP глаголу POST. На 16 строке  строим маршрут типа “http://localhost:3000/users/{_id}” (например, “http://localhost:3000/users/789w66s2322kks4676s” ). Данный маршрут будет доступен по HTTP глаголу GET. На 23 строке  строим маршрут типа “http://localhost:3000/users/{_id}” (например, “http://localhost:3000/users/789w66s2322kks4676s” ). Данный маршрут будет доступен по HTTP глаголу PUT. На 30 строке  строим маршрут типа “http://localhost:3000/users/{_id}” (например, “http://localhost:3000/users/789w66s2322kks4676s” ). Данный маршрут будет доступен по HTTP глаголу DELETE. Откройте модуль “app.js”, обновите его в соответствии с содержимым ниже: Конкретно в модуль “app.js”  были внесены следующие изменения. С 8 по 11 строку  были подключены модули “index.js”, “users.js”, “products.js”, “auth.js”. На 13 строке  был подключен модуль “mongoose”. На 14 строке  мы задаем свойство “Promise” в значение “global.Promise”, чтобы начать обмениваться сообщениями с базой данной “Mongo DB”. На 15 строке  мы подключаемся к базе данных “Mongo DB” c названием “warehouse”, мы ее ранее создавали в приложении “Robomongo”. Источник данных “mongodb://localhost/warehouse” можно разбить так: “mongodb://” протокол взаимодействия (аналоги “http://”, “tcp://”, “file://”). “localhost” - это адрес машины, на которой хостится база данных. “warehouse” - имя базы данных. На 16 строке  метод “then”  вызывается в случае успешного подключения к базе данных. На 17 строке  метод “catch”  вызывается в случае сбоя подключения к базе данных. С 33 по 36 строку мы задаем доступные глобальные маршруты, которые будет обрабатывать сервер. Надеюсь, все помнят разницу между сервером и сервисами, это не одно и то же. Сервер - это просто приложение, которое внутри себя способно содержать различные ресурсы. Сервер может запускать внутри себя различные потоки для обработки данных. Сервис - это функционал, который как раз запускается в отдельном потоке внутри сервера. Сам по себе сервис без наличия приложения ,в котором его можно разместить, работать не будет. В случае с WebAPI сервером является Microsoft IIS или отдельно стоящее исполняемое приложение с функцией self  хостинга. Мы закончили написание сервера. Запустите демон “mongod” в терминале. Запустите сервер. Вы увидите обновленное приветствие. Я бы рекомендовал вам прослушать курс “Angular JS”, автор Дмитрий Охрименко. Это один из лучших курсов, которые я когда-либо слушал. После данного курса вы без особых проблем сможете сделать клиентскую часть для сервера на “Angular JS”. Теперь давайте научимся тестировать наши REST сервисы. Тестировать сервисы можно и через Fiddler, но на macOS  у него есть проблемы с отрисовкой и работой. Поэтому откройте браузер и перейдите по адресу. “Postman” - это аналог “Fiddler”, доступный в качестве расширения для браузера “Google Chrome”. После установки расширения “Postman” откройте его. Выполним первый запрос к сервису “users” по HTTP глаголу GET. Заполните поле адрес, выберите глагол “GET”, нажмите кнопку “Send”. Результат придет - пусто. Обратите внимание, что в терминале “WebStorm” тоже пишется логирование вызовов при обращения к сервисам. Откройте приложение “Robomongo”, подключитесь к базе данных. Разверните папку “Collections” базы данных “warehouse”, выполните щелчок правой кнопкой мыши по таблице “users”, в контекстном меню выберите “View Documents”. В “Mongo DB” нет колонок, там есть всего одна колонка, в которой хранится JSON объект. Строки называются документами. Причем, в каждую строку одной таблицы можно класть абсолютно разный по структуре JSON объект. База данных позволяет такую операцию. Видим, что данных нет, это не удивительно, ведь мы еще ничего не добавляли. Давайте добавим. Перейдите в “Postman”, выберите глагол “POST”. Выберите “Content-Type” - “x-www-form-urlencoded”. Внимание, через другие типы “form-data”  “raw” работать не будет. Заданные свойства “userName”, “userPwd”, “userDesc” будут отправлены в запросе как JSON объект “{ userName: 'admin', userPwd: 'abc123!', userDesc: 'Administrator' }”. Нажмите кнопку “Send”. Придет ответ: В ответе мы видим JSON объект модели “User”, созданной на сервере. Перейдите в приложение “Robomongo”, выполните пункт контекстного меню “View Documents” на таблице “users”. Отлично, у нас появился первый пользователь :) Перейдите в “Postman”, выберите HTTP глагол “GET”. Нажмите кнопку “Send”. Вы увидите такой результат. Обратите внимание на квадратные скобки, в ответе пришел массив JSON объектов, а не один объект. Теперь давайте изменим пароль пользователю “admin”, на “12345678”. Нам понадобится идентификатор пользователя, выделите и скопируйте значение свойства “_id”. Выберите HTTP глагол “PUT”. Обновите данные так, как показано ниже. Обратите внимание на добавление идентификатора пользователя после основного маршрута “../users/”. Нажмите кнопку “Send”. В ответе получим старый JSON объект до внесения изменений. Теперь для того, чтобы посмотреть измененный объект, воспользуемся сервисом , который возвращает пользователя по его идентификатору “_id”. Выберите HTTP глагол “GET”, обновите адрес, добавьте в конец адреса идентификатор пользователя. Нажмите кнопку “Send”. Получим такой результат. Видим, что пароль пользователя был успешно обновлен и мы получили в ответе один JSON объект, а не массив пользователей. Теперь попробуем удалить пользователя. Адрес оставьте тот же. Выберите HTTP глагол “DELETE”  и нажмите кнопку “Send”. Видим, что в ответе содержится JSON объект удаленного пользователя.             Теперь проверим, доступен ли пользователь после удаления. Адрес оставьте тот же. Выберите HTTP глагол “GET”, нажмите кнопку “Send”. Видим, что пользователь действительно был удален. Также можете проверить это в приложении “Robomongo”.             Теперь приступим к тестированию сервиса “products”.             Получим список товаров. Товаров нет. Добавим первый товар. Обновим описание товара. Получим обновленный товар по его идентификатору. Удалим товар. На этом мы завершаем урок. К данной статье прикреплен архив с готовым сервером на NodeJS , в помощь  учащимся :) На следующем уроке мы: выполним рефакторинг проекта “Warehouse” под “iOS”; научимся пользоваться CollectionViewController; научимся пользоваться библиотекой “Alamofire”, в конце урока будут доступны исходники полноценного рабочего приложения; научимся сохранять объекты в настройки телефона и извлекать их; реализуем функциональность для работы со всеми сервисами, созданными в данном уроке; добавим представление регистрации; добавим представление редактирования пользователей. Материалы к статье тут.
Notification success