Асинхронный javaScript, AJAX, API

Синхронный код и асинхронный

Синхронный код

  • В основном весь код синхронный
  • Синхронный код выполняется строка за строкой
  • Каждая строка кода всегда ждёт когда закончится выполнение предыдущей строки
  • Длительные операции блокируют выполнение кода
// поток выполнения идет последовательно от строки к строке
const div = document.querySelector('div'); // запускается эта строка
div.innerHTML = <p>Всем привет</p> // после запускается эта строка
console.log(div); // после запускается эта строка и так далее

// Такие длительные операции как alert блокируют код
alert(div.textContent  = 'Всем привет');
// пока мы не нажем ok на появившемся окне, выполнение кода будет заблокировано

Поэтому если у нас есть длительная операция на сайте, то весь код будет заблокирован и мы ничего не сможем сделать. Для таких ситуаций нам и нужен асинхронный код.

Асинхронный код

  • Асинхронно - значит не в тоже время
  • Асинхронный код запускается после того, как задача выполняемая в "фоновом режиме" завершается
  • Асинхронный код является неблокирующим
  • Поток выполнения не ждет, пока асинхронная операция завершит свою работу
  • Callback функции сами по себе не делают код асинхронным

Операция с callback функцией:

const div = document.querySelector('div'); // синхронный код
setTimeout(()=> { // асинхронный код
    div.textContent = 'Всем пока';
},2000)

div.textContent = 'Всем привет' // синхронный код
  1. Мы выполним первую строку синхронную операцию
const div = document.querySelector('div'); 
  1. Далее идет setTimeout и код не остановит свое выполнение, запуск таймера будет в фоновом режиме. Поток выполнения просто регистрирует эту функцию и сразу выполняет следующую строку кода.
setTimeout(()=> { 
    div.textContent = 'Всем пока';
},2000) // 2000 миллисекунд - 2 секунды

// Пока таймер отсчитывает время, выполнение кода не останавливается, поэтому это неблокирующий код.
  1. Далее мы выполним последнею строчку кода.
div.textContent = 'Всем привет';
  1. Через 2 секунды выполнится и колбек функция внутри таймера.
div.textContent = 'Всем пока'; // наша строка из setTimeout

Callback - универсальный

callback функции не есть обязательно асинхронные, есть также и синхронные - они универсальны.
Пример синхронного callback с MDN:

const gods = ['Apollo', 'Artemis', 'Ares', 'Zeus'];

gods.forEach(function (eachName, index){ // callback функция ничего не ждет, она выполняется сразу
    console.log(index + '. ' + eachName);
});

Что за AJAX такой

AJAX — это аббревиатура, которая означает Asynchronous Javascript and XML. AJAX позволяет нам асинхронно работать с веб-серверами. С помощью AJAX мы можем динамически запрашивать данные с веб-серверов. Если понятнее то, мы можем получить данные, при этом страница не будет обновляться.

Как работает ajax

Клиент(браузер) создает запрос(request - get/post), на веб-сервер и мы получаем ответ(response) отправку данных - все это происходит в асинхронном режиме. Работает это с помощью некого API которое работает на сервере и принимает запросы на данные и отправляет данные обратно в качестве ответа. Подробнее тут .

Что такое api

API(Application programming interface) - это часть программы, которая может использоваться другой программой, чтобы приложения могли взаимодействовать друг с другом. В веб-разработке существует множество типов API. Например DOM API - с помощью которого мы взаимодействуем с html. Объект document - это интерфейс DOM API. XMLHttpRequest с которым мы будем работать дальше - это тоже api, набор готовых методов и решений.

Get и Post - запросы

Get

Get запрос - направлен на то, что бы получить какие то данные от сервера, в нашем случае мы будем получать курс валюты у нашего current.json бекенд части (сервер) get запросы показывают товары например в магазине на сайте.

Post

Post - это постить запросы, например регистрация на сайте, мы вводим наши данные и постим их на сервер, или например когда загружаем файлы.

Не современный способ AJAX - XMLHttpRequest

XMLHttpRequest уже устаревшее api, сейчас используется fetch. Но XMLHttpRequest все еще может встретиться, так же разобравшись в нем fetch не станет проблемой.
Нашим сервером будет файл current.json из него мы будем получать курс доллара.

{
    "current":{
        "usd":74
    }
}

У нас будет два инпута.

<div class="calc">
    <label for="rub">RUB</label>
    <input id="rub" type="text">
    <label for="usd">USD</label>
    <input id="usd" type="text">
</div>
console.log

Получаем элементы и погнали:

const inputRub = document.querySelector('#rub'),
    inputUsd = document.querySelector('#usd');


inputRub.addEventListener('input', () => { // событие input сробатывает когда в инпуте что-то меняется
    const request = new XMLHttpRequest(); // создали экземпляр с нужными нам свойствами и методами
    // XMLHttpRequest - это объект в которомд находятся свойства и методы.


    // open(); - этот метод собирает настройки которые в будущем помогут сделать запрос. принимает в себя несколько аргументов.
    request.open('GET', 'src/current.json'); // вызываем open() у нашего объекта. Внутрь попещаем аргументы:
    /* 
    1 - method: это тот который используется для запроса(get post и тд)
    2 -  url: это путь к нашему сервевру. Путь у url строим относительно index.html.
    3 - async: этот аргумент отвечает за асинхронность - в нем по умолчанию стоит true, можн опоставить false, но тогда это уже будет синхронный код
    4 - login - логин: некоторые запросы мы можем делать только имея пароль и логин 
    5 - pass  - пароль
    */
    // Далее вызываем setRequestHeader() - устанавливает значения HTTP заголовков. Вызывается после open() но до send(). 
    // далее send() - отправляем наш запрос.  send: принимает в себя body - это данные которые уходят на сервер, но это в Post запросе в get этого нет, потому что мы получаем их. Поэтому send вызываем, но внутрь ничего не передаем.
request.setRequestHeader('Content-type', 'application/json; charset=utf-8'); // говорим что есть опр тип - тут уже сам тип это наш json и далее кодировка, самая стандартная.
request.send(); - просто вызваем.

});

Свойства и коды ответа http

В этих свойствах мы получаем ответ полсе запроса на сервер:

  1. status - показывает статус нашего запроса, это 404 - файла нет. 0, 200, 403 и тд. Все мы это видили в браузере
  2. statusText - это текстовое описание от сервера.
  3. response - это ответ от сервера, это то что нам ответил бекенд разработчик, то что мы должны использовать на клиенте.(у нас это json Объект)
  4. readyState - возвращает текущее состояние объекта, нашего запроса.

Зачения кодов в status:

  • Информационные: 100 - 199
  • Успешные: 200 - 299
  • Перенаправления: 300 - 399
  • Клиентские ошибки: 400 - 499
  • Серверные ошибки: 500 - 599

Коды http запросов легко гуглятся. Например код 404 - Not Found. Категория от 400 - 499 относятся к клиенским ошибкам, и тд..

readyState:

readyState будет возвращать вот такие состояния, где цифры это значение, далее состояние и описание:

0 - UNSENT: Объект был создан. Метод open() ещё не вызывался.
1 - OPENED: Метод open() был вызван.
2 - HEADERS_RECEIVED: Метод send() был вызван, доступны заголовки (headers) и статус.
3 - LOADING: Загрузка. responseText содержит частичные данные.
4 - DONE: Операция полностью завершена.

События объекта XMLHttpRequest

readystatechange

readystatechange - следит за свойством readyState о котором говорили выше. Например когда запрос изменится с 0 на 1 - сработает событие. Все это нам позволяет написать условие внутри нашего запроса, что мы писали выше.

request.addEventListener('readystatechange',()=>{  // вызываем событие readystatechange у объекта request.
    if (request.readyState === 4 && request.status === 200) { // тут мы узнаем, если у нас 4 - операция завершена и статус 200 - это значит Ок - хорошо
        // Так же чаше использует событие load оно срабатывает один раз когда запрос готов.
        // мы удалем  request.readyState === 4 &&
        // остовляя только request.status === 200, а cобытие меняем readystatechange на load
        console.log(request.response); // выведем наш объект в консоль. Что бы наглядно увидеть, что это наш json 
        const data = JSON.parse(request.response); // в data помещаем наш JSON объект и сразу парсим в обычный. он находится в response - ответ от сервера.
        inputUsd.value = (+inputRub.value / data.current.usd).toFixed(2); // в инпут usd выводим инпут с рублем деленый на значение usd из нашего объекта
        

    }
    else {
        inputUsd.value = 'что-то пошло не так';
    }
});

Теперь все вместе без моих комментариев:

const inputRub = document.querySelector('#rub'),
    inputUsd = document.querySelector('#usd');


inputRub.addEventListener('input', () => {
    const request = new XMLHttpRequest();

    request.open('GET', 'src/current.json');

    request.setRequestHeader('Content-type', 'application/json; charset=utf-8');
    request.send();
    request.addEventListener('readystatechange', () => {
        if (request.readyState === 4 && request.status === 200) {
            const data = JSON.parse(request.response);
            console.log(request.response);
            inputUsd.value = (+inputRub.value / data.current.usd).toFixed(2);


        } else {
            inputUsd.value = 'что-то пошло не так';
        }
    });
});

Калькулятор

И вот, что получаем: