JavaScript - Асинхронные AJAX запросы на примерах

Александр Мальцев
74K
0
Содержание:
  1. Что такое асинхронный запрос AJAX
  2. Создание асинхронного AJAX запроса (метод GET)
  3. Создание асинхронного AJAX запроса (метод POST)
  4. Комментарии

Урок, в котором на примерах рассмотрим создание простых асинхронных AJAX запросов к серверу. В качестве метода передачи запросов будем использовать как метод GET, так и метод POST. На сервере обработку запросов выполним с помощью скриптов PHP.

Что такое асинхронный запрос AJAX

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

При отправке асинхронного запроса, браузер (страница) не "замораживается", т.е. с ней, как и прежде, можно работать. Но тогда как узнать, когда придёт ответ с сервера. Чтобы это определить, необходимо отслеживать свойство браузера readyState (состояние готовности). Данное свойство содержит число, по значению которого можно судить о том, в какой стадии находится запрос. В следующей таблице приведены основные значения свойства readyState и соответствующие им состояния.

Значение свойства readyState Описание
0 запрос не инициализирован
1 выполнена настройка запроса
2 запрос отправлен
3 запрос находится в процессе обработки на сервере
4 запрос завершён

Т.е. получается, что нам необходимо отследить, когда значение у свойства readyState будет равно 4. Это будет означать то, что на отправленный запрос, пришёл ответ с сервера. Остальные значения на практике используются довольно редко, и некоторые браузеры могут их не поддерживать.

Для того чтобы определить на какой стадии находится запрос, необходимо использовать событие объекта XMLHttpRequest onreadystatechange. Данное событие происходит каждый раз, когда изменяется значение свойства readyState. Следовательно, в обработчике этого события (неименованной или именованной функции) можно прописать действия, которые будут проверять равно ли данное свойство 4 и если равно, то например, вывести ответ сервера на страницу.

Схема работы AJAX (асинхронный запрос)
Схема работы AJAX (асинхронный запрос)

Создание асинхронного AJAX запроса (метод GET)

Рассмотрим создание асинхронного AJAX запроса на примере, который будет после загрузки страницы приветствовать пользователя и отображать его IP-адрес.

Для этого необходимо создать на сервере 2 файла в одном каталоге:

  1. welcome.html – HTML-страница, которая будет отображаться пользователю. В этой же страницы поместим скрипт, который будет осуществлять все необходимые действия для работы AJAX на стороне клиента.
  2. processing.php – PHP-файл, который будет обрабатывать запрос на стороне сервера, и формировать ответ. Начнём разработку с создания основной структуры файла welcome.html
<!DOCTYPE html>
<html lang="ru">
<head>
  <meta charset="utf-8">
  <title>Пример работы AJAX</title>
</head>
<body>
  <h1>Пример работы AJAX</h1>
  <div id="welcome"></div>
  <script>
  // Здесь поместим код JavaScript, который будет отправлять запрос на сервер, получать его и обновлять содержимое страницы. Всё это будет работать без перезагрузки страницы

</script>
</body>
</html>  

Рассмотрим последовательность действий, которые необходимо выполнить на стороне клиента (в коде JavaScript):

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

  2. Создадим переменную, которая будет содержать экземпляр объекта XHR (XMLHttpRequest).

  3. Настроим запрос с помощью метода open().

    Указываются следующие параметры:

    • Метод, с помощью которого будет посылаться запрос на сервер (GET, POST).
    • URL-адрес, который будет обрабатывать запрос на сервере.
    • Тип запроса: синхронный (false) или асинхронный (true).
    • Имя и пароль при необходимости.
  4. Подпишемся на событие onreadystatechange объекта XHR и укажем обработчик в виде анонимной или именованной функции. После этого создадим код внутри этой функции, который будет проверять состояние ответа, и выполнять определённые действия на странице. Ответ, который приходит с сервера, всегда находится в свойстве responseText.

    Дополнительно с проверкой значения свойства readyState числу 4, можно проверять и значение свойства status. Данное свойство определяет статус запроса. Если оно равно 200, то всё OK. А иначе произошла ошибка (например, 404 – URL не найден).

  5. Отправим запрос на сервер с помощью метода send().

    Если используем для отправки запроса метод GET, то передавать данные в параметр данного метода не надо. Они передаются в составе URL.

    Если используем для отправки запроса метод POST, то данные необходимо передать в качестве параметра методу send(). Кроме этого, перед вызовом данного метода необходимо установить заголовок Content-Type, чтобы сервер знал в какой кодировке пришёл к нему запрос и смог его расшифровать.

Содержимое элемента script:

// 2. Создание переменной request
var request = new XMLHttpRequest();
// 3. Настройка запроса
request.open('GET','processing.php',true);
// 4. Подписка на событие onreadystatechange и обработка его с помощью анонимной функции
request.addEventListener('readystatechange', function() {
  // если состояния запроса 4 и статус запроса 200 (OK)
  if ((request.readyState==4) && (request.status==200)) {
    // например, выведем объект XHR в консоль браузера
    console.log(request);
    // и ответ (текст), пришедший с сервера в окне alert
    console.log(request.responseText);
    // получить элемент c id = welcome
    var welcome = document.getElementById('welcome');
    // заменить содержимое элемента ответом, пришедшим с сервера
    welcome.innerHTML = request.responseText;
  }
}); 
// 5. Отправка запроса на сервер
request.send();

В итоге файл welcome.html будет иметь следующий код:

<!DOCTYPE html>
<html lang="ru">
<head>
  <meta charset="utf-8">
  <title>Пример работы AJAX</title>
</head>
<body>
  <h1>Пример работы AJAX</h1>

  <div id="welcome"></div>

<script>
window.addEventListener("load",function() {
  var request = new XMLHttpRequest();
  request.open('GET','processing.php',true);
  request.addEventListener('readystatechange', function() {
    if ((request.readyState==4) && (request.status==200)) {
      var welcome = document.getElementById('welcome');
      welcome.innerHTML = request.responseText;
    }
  });
request.send();
});
</script>

</body>
</html>

На сервере (с помощью php):

  1. Получим данные. Если данные посланы через метод GET, то из глобального массива $_GET['имя']. А если данные переданы с помощью метода POST, то из глобального массива $_POST['имя'].
  2. Используя эти данные, выполним некоторые действия на сервере. В результате которых получим некоторый ответ. Выведем его с помощью echo.
<?php
$output = 'Здравствуйте, пользователь! ';
if ($_SERVER['REMOTE_ADDR']) {
  $output .= 'Ваш IP адрес: '. $_SERVER['REMOTE_ADDR'];
}
else {
 $output .= 'Ваш IP адрес неизвестен.';
}
echo $output;
HTML-страница, на которую пришёл AJAX ответ с сервера (метод GET)
HTML-страница, на которую пришёл AJAX ответ с сервера (метод GET)

Создание асинхронного AJAX запроса (метод POST)

Изменим вышеприведённый пример. Теперь AJAX запрос к серверу будет выполняться после нажатию на кнопку. Он будет получать имя, которое ввёл пользователь в элемент input и отправлять его посредством метода POST на сервер. После получения ответа с сервера, заменим им содержимое элемента div на странице.

HTML:

<!DOCTYPE html>
<html lang="ru">
<head>
  <meta charset="utf-8">
  <title>Пример работы AJAX</title>
</head>
<body>
  <h1>Пример работы AJAX</h1>

  <div id="welcome">
    <label for="name">Имя:</label>
    <input type="text" id="name" placeholder="Например, Иван">
    <button id="send">Отправить</button>
  </div>

<script>
//после загрузки DOM-дерева страницы
document.addEventListener("DOMContentLoaded",function() {
  //получить кнопку
  var mybutton = document.getElementById('send');
  //подписаться на событие click по кнопке и назначить обработчик, который будет выполнять действия, указанные в безымянной функции
  mybutton.addEventListener("click", function(){
    //1. Сбор данных, необходимых для выполнения запроса на сервере
    var name = document.getElementById('name').value;
    //Подготовка данных для отправки на сервер
    //т.е. кодирование с помощью метода encodeURIComponent
    name = 'nameUser=' + encodeURIComponent(name);
    // 2. Создание переменной request
    var request = new XMLHttpRequest();
    // 3. Настройка запроса
    request.open('POST','processing.php',true);
    // 4. Подписка на событие onreadystatechange и обработка его с помощью анонимной функции
    request.addEventListener('readystatechange', function() {
      //если запрос пришёл и статус запроса 200 (OK)
      if ((request.readyState==4) && (request.status==200)) {
        // например, выведем объект XHR в консоль браузера
        console.log(request);
        // и ответ (текст), пришедший с сервера в окне alert
        console.log(request.responseText);
        // получить элемент c id = welcome
        var welcome = document.getElementById('welcome');
        // заменить содержимое элемента ответом, пришедшим с сервера
        welcome.innerHTML = request.responseText;
      }
    });
    // Устанавливаем заголовок Content-Type(обязательно для метода POST). Он предназначен для указания кодировки, с помощью которой зашифрован запрос. Это необходимо для того, чтобы сервер знал как его раскодировать.
    request.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded')
    // 5. Отправка запроса на сервер. В качестве параметра указываем данные, которые необходимо передать (необходимо для POST)
    request.send(name);
  });
});
</script>

</body>
</html>

PHP:

<?php
// создадим переменную для формирования ответа
$output ='';
// если в массиве пост есть ключ nameUser, то
if (isset($_POST['nameUser'])) {
  // в переменную name помещаем переданное с помощью запроса имя
  $name = $_POST['nameUser'];
  // добавляем в переменную output строку приветствия с именем
  $output .= 'Здравствуйте, '.$name.'! ';
  // добавляем в переменную output IP-адрес пользователя
  if ($_SERVER['REMOTE_ADDR']) {
    $output .= 'Ваш IP адрес: '. $_SERVER['REMOTE_ADDR'];
  }
  else {
   $output .= 'Ваш IP адрес неизвестен.';
  }
  // выводим ответ
  echo $output;
}
HTML-страница с AJAX (метод POST)
HTML-страница с AJAX (метод POST)
HTML-страница, на которую пришёл AJAX ответ с сервера (метод POST)
HTML-страница, на которую пришёл AJAX ответ с сервера (метод POST)

Комментарии ()

  1. Alexander
    4 минуты назад
    Александр, доброго здоровья!

    Будьте добры, подскажите один момент насчет аякс-запросов к стороннему сайту (не локалхост).

    Такой вот код
    $.ajax({
          url: "http://.......",
          method: "POST",
          timeout: 0,
          crossDomain:true,
          data: "{\"sel\":\"production\"}",
            beforeSend: function(xhr) { 
                  xhr.setRequestHeader("Authorization", "Basic " + btoa(name+":"+pwd)); 
                },
            success: function (data) {
              console.log(data);
            }
        });
    
    В отладчике вызывает ошибку:

    «Cross-Origin Request Blocked: The Same Origin Policy disallows reading the remote resource at … (Reason: CORS header ‘Access-Control-Allow-Origin’ missing).»

    Аналогичная ситуация и при испольозвании
    var data = "{\"sel\":\"production\"}";
    var xhr = new XMLHttpRequest();
    xhr.withCredentials = true;
    xhr.addEventListener("readystatechange", function() {
      if(this.readyState === 4) {
        console.log(this.responseText);
      }
    });
    xhr.open("POST", "http://....");
    xhr.send(data);
    
    Так как через reqbin.com все работает, прричина все-таки не в настройках сервера?
    1. autotester
      22 апреля 2020, 12:35
      У меня на платформе один фрейм и в нем меняются страницы (без перезагрузки). Сейчас возникла проблема, что страница не успевает загрузиться и коллега подсказал проверить ajax запрос, но как это сделать — не знаю. Вручную проблема не воспроизводится, только при автотесте. Можете подсказать, как можно посмотреть эти запросы и понять, где проблема?
      1. Александр Мальцев
        22 апреля 2020, 16:21
        Открываете «Инструменты разработчика» в браузере (в Windows комбинация клавиш Ctrl + Shift + I). В ней переходите на вкладку Network, при необходимости можете включить показ только XHR (для этого нужно нажать на соответствующую кнопку на строке Filter), чтобы не показывались другие записи. Далее выбираете нужный скрипт и просматриваете информацию: Headers, Response и т.д. В большинстве случаев этой информации для анализа должно быть достаточно.
      2. Beginner
        09 декабря 2019, 20:14
        Зравствуйте, Александр.

        Подскажите пожалуйста что специфичное (нестандартное) передается при асинхронных запросах:

        — в заголовках от клиента к серверу и обратно

        — в данных от клиента к серверу при POST запросе, и от сервера клиенту в блоке данных

        Вопрос связан с тем, что HTTP-сервер самописанный и встроен в программу (бинарник), поэтому использовать примеры на PHP не представляется возможным. Cервер работает напрямую с сокетами, делает разбор и формирование заголовков и данных.

        Заранее спасибо.
        1. Beginner
          10 декабря 2019, 22:59
          Спасибо, Александр, за ваш ответ.

          Ваш пример заработал. Действительно, асинхронные запросы прозрачны для сервера.
          Моя ошибка была в том, что я выдавал статус 302, а не 200. Это было связано с тем, что нижеприведенный код срабатывал только на статус 302.

          <!DOCTYPE html>
          <html>
          <script>
          'use strict';
          
          (async () => {
          let url = 'https://api.github.com/repos/javascript-tutorial/en.javascript.info/commits';
          let response = await fetch(url);
          
          let commits = await response.json();  // или  .text()  
          
          alert(commits[0].author.login);
          })()
          </script>
          </html>
          
          1. Александр Мальцев
            10 декабря 2019, 15:16
            Привет! AJAX запросы (синхронные или асинхронные) к серверу ничем не отличаются от обычных. Поэтому если вы не установите с помощью JavaScript дополнительный заголовок, то на сервере вы никак не сможете узнать, что это AJAX. Например, библиотека jQuery устанавливает следующий дополнительный заголовок к запросу: «X-Requested-With: XMLHttpRequest».
          2. selftrips
            24 ноября 2019, 06:58
            Напишите как именно работать с
            На сервере (с помощью php):

            Получим данные. Если данные посланы через метод GET, то из глобального массива $_GET['имя']. А если данные переданы с помощью метода POST, то из глобального массива $_POST['имя'].
            Используя эти данные, выполним некоторые действия на сервере. В результате которых получим некоторый ответ. Выведем его с помощью echo.

            <?php
                $output = 'Здравствуйте, пользователь! ';
                if ($_SERVER['REMOTE_ADDR']) {
                  $output .= 'Ваш IP адрес: '. $_SERVER['REMOTE_ADDR'];
                }
                else {
                 $output .= 'Ваш IP адрес неизвестен.';
                }
                echo $output;
            1. Александр Мальцев
              24 ноября 2019, 11:23
              Тут всё зависит от задачи.
              Первое — это нужно получить данные (GET, POST, REQUEST). После этого обычно приступают к проверке данных и того, как они были получены. Если валидация данных прошла не успешно, то действие на сервере обычно прекращают и возвращают результат. В противном случае продолжают дальнейшие действия. Здесь уже вы выполняете действия в зависимости от задачи, опираясь на данные, которые получили. Это может быть выбор информации из базы данных или запись в неё, отправка писем, запись данных в файлы и многое другое. В результате выполнения этого или нет необходимо сформировать ответ и отправить его пользователю. При AJAX формирование ответа обычно осуществляется в формате JSON.
              После этого, когда клиент (браузер пользователя) получит ответ от сервера, необходимо его разобрать и вывести пользователю данные или какую-то другую информацию.

              Чтобы глубже разобраться в этом разбирайте реальные примеры: Скрипт звёздного рейтинга для сайта, Форма обработной связи и др.
              1. selftrips
                24 ноября 2019, 14:10
                А ваш пример нельзя дополнить, чтобы получить как у вас в статье

                так как реально введенное имя назад не возвращается.
                1. Александр Мальцев
                  24 ноября 2019, 14:40
                  Этот момент добавлен к статье.
                  1. selftrips
                    24 ноября 2019, 15:31
                    теперь вообще не работает
                    1. selftrips
                      24 ноября 2019, 14:54
                      myexpedia.ru/%d1%81%d0%ba%d1%80%d0%b8%d0%bf%d1%82/
                      ps
                      Еще в ваше статье не корректно работает блок развернуть — перекрывает текст ниже, а должен сдвигать вниз
                      1. selftrips
                        24 ноября 2019, 14:47
                        не работает php не получает введеное имя, соответственно не может вывести (метод post)
                2. Влад
                  12 марта 2017, 20:45
                  Здравствуйте, у меня не получается сделать так, чтобы по нажатию на кнопку изменялась переменная в скрипте php и выводились данные вместо div'а. Сами переменные объявлены в другом куске скрипта.
                  <script>
                  <button id="tr" style = "background-color: mediumspringgreen; width: 120px; font-size:20px; height: 35px"  onclick="color('lightgreen')"><?php echo $string; ?></button>
                  
                  document.addEventListener("load", function() {
                    var mybutton = document.getElementById('tr');
                       mybutton.addEventListener("click", function(){
                      var request = new XMLHttpRequest();
                      request.open('GET','scr3.php',true);
                      request.addEventListener('readystatechange', function() {
                        if ((request.readyState==4) && (request.status==200)) {
                        var welcome = document.getElementById('welcome');
                        welcome.innerHTML = request.responseText; 
                        }
                      });
                      request.send();
                    });
                  });
                  </script>
                  
                  </div id="welcome">
                      <?php
                       echo $true;?> out of <?php echo $all; ?>
                    </div> 
                  
                  <?php
                  $output ='';
                    $output .= ++$true . ' of ' . ++$all;
                    echo $output;
                          ?>
                  
                  1. Влад
                    12 марта 2017, 22:29
                    Нашел: как обычно не работало из-за какой-то фигни(
                    </div id="welcome">
                        <?php
                         echo $true;?> out of <?php echo $all; ?>
                      </div>
                    Два раза див закрывал, вместо того, чтобы открывать-закрывать:(
                  2. Jaan
                    01 марта 2017, 12:48
                    Александр здравствуйте! помогите уяснить один момент. После отправки методом POST с welcome.html на страницу proccesing.php на самой странице proccesing.php можно отобразить переданные данные? Ведь страница proccesing.php не перезагружается. Или же увидеть переданные данные так нельзя?
                    1. Александр Мальцев
                      01 марта 2017, 16:22
                      Здравствуйте. Вызов файла proccesing.php в данном случае осуществляется посредством AJAX. Т.е. мы не открываем его явно как страницу.
                      Для того чтобы данная страница (proccesing.php) что-то отобразила ей необходимо передать данные методом POST. Т.е. она не может ничто вывести, если в суперглобальном массиве $_POST не будет ключа nameUser. Другими словами, вам никто не запрещает сделать обычную HTML форму и назначить данный файл для её обработки (без AJAX).

                      Но если вы хотите чтобы файл proccesing.php выполнялся только для AJAX запросов, то необходимо сделать следующее:
                      1. Добавить в сценарий JavaScript строчку:
                      request.open('POST','processing.php',true);
                      // добавим специальный заголовок
                      request.setRequestHeader("X-Requested-With", "XMLHttpRequest");
                      
                      2. Будем его проверять в файле proccesing.php. Если данный заголовок не доступен в этом файле (запустили его не по AJAX), то завершаем его работу.
                      <?php
                      // если запрос не является AJAX, то завершить работу
                      if (empty($_SERVER['HTTP_X_REQUESTED_WITH']) || $_SERVER['HTTP_X_REQUESTED_WITH'] != 'XMLHttpRequest') {
                        exit();
                      }
                      // ... остальной код
                      
                      1. Jaan
                        01 марта 2017, 19:15
                        Что-то не получается. Все равно не выводятся данные на странице proccesing.php. Немного опишу ситуацию что я пытаюсь сделать. На сайте в файле index.html (условно) подключен скрипт js который кроппит обрезает изображение. Потом с помощью кнопки в форме по нажатию на которою js просто вставляет обрезанное изображение в пустой div.

                        Мне нужно по нажатию на эту кнопку что бы изображение отсылалось на сервер. В идеале конечно в контроллер отослать как будто через обыкновенную форму.
                        1. Александр Мальцев
                          02 марта 2017, 15:14
                          Чтобы после отправки открывалась страница proccesing.php необходимо отправлять данные без AJAX, т.е. с помощью формы:
                          <form action="processing.php" method="POST">
                            <label for="nameUser">Имя:</label>
                            <input type="text" name="nameUser" id="name" placeholder="Например, Иван">
                            <button type="submit" id="send">Отправить</button>
                          </form>
                          
                          При этом код JavaScript не нужен.

                          Как отправлять изображения на сервер можете посмотреть в этой статье: itchief.ru/lessons/php/feedback-form-for-website
                          1. Jaan
                            02 марта 2017, 19:04
                            Спасибо Александр за статью надеюсь это то что мне нужно.
                        2. Jaan
                          01 марта 2017, 18:59
                          Спасибо Александр пошел пробовать код.
                      2. Евгений
                        20 января 2016, 15:42
                        Добрый день.
                        Большое спасибо за статью!
                        А где бы посмотреть подгрузку информации при прокрутке страницы? Т.е. например листаем вниз, добавляются новые посты.
                        1. Александр Мальцев
                          23 января 2016, 04:12
                          Здравствуйте.
                          Тут необходимо завести переменную, которая будет отвечать за то, находится ли AJAX запрос в обработке или нет. Например, processing. Т.е. если при прокрутке страницы, значение переменной равно false, то загружаем данные. На момент загрузки данных устанавливаем значение переменной processing, равной true. Это необходимо сделать для того чтобы не сделать кучу запросов пока данные не пришли. Как только данные пришли, устанавливаем переменной значение false и готовы к загрузке новой партии данных с сервера. Другими словами, данная переменная необходима для того чтобы при прокрутке Вы не сделали 1000 запрос к серверу, а только один.

                          Для простоты приведу решение на jQuery, пояснив каждую строчку кода. Для загрузки данных будем использовать содержимое файла data.txt. Т.е. при прокрутке страницы более чем на 80%, берём данные из файла data.txt и помещаем их в блок div.

                          <!-- Контейнер, в который будут подгружаться данные из файла data.txt -->
                          <div id="container"></div>
                          
                          <script>
                          // переменная, отвечаяющая за то находится ли в процесс в получение данных с помощью AJAX
                          var processing;
                          // после загрузки страницы
                          $(document).ready(function(){
                            // при прокрутке страницы
                            $(document).scroll(function(e){
                              // если процесс находится в обработке (т.е. ещё не данные не пришли), то ничего не делать
                              if (processing)
                                return false;
                              // если пользователь прокрутил страницу более чем на 80%
                              if (window.scrollTop >= (document.height - window.height)*0.8){
                                // отмечаем то, что процесс находится в обработке
                                processing = true;
                                // отправляем и обрабатываем запрос
                                $.get('data.txt', function(data){
                                  // вставляем контент в контейнер
                                  $('#container').append(data);
                                  // отмечаем, что процесс получения данных завершился
                                  processing = false;
                                });
                              }
                            });
                          });
                          </script>
                          
                          Для демонстрации можно создать файл data.txt со следующим содержимым:
                          <div class="content">Информация :)</div>
                          
                          Ну и добавить стиль CSS в вышеприведённый документ:
                          <style>
                          .content {
                            min-height: 1280px;
                          }
                          </style>
                          
                          1. Евгений
                            26 января 2016, 03:03
                            Огромное спасибо!!!
                        Войдите, пожайлуста, в аккаунт, чтобы оставить комментарий.