Сравниваем различные способы выполнения HTTP-запросов в JavaScript

Недавно мне нужно было выбрать, какую технологию для выполнения вызовов ajax использовать в большом JavaScript проекте. В JavaScript есть различные способы выполнения запросов. 

Ранее существовавшие методы получения данных с сервера без перезагрузки страницы полагались на ненадёжные методы. Microsoft разработал XMLHttpRequest как альтернативу для браузеров для своего почтового клиента Outlook. В 2006 году XMLHttpRequest стал веб-стандартом.

Fetch API был представлен в 2015 году с ES6. Универсальные интерфейсы запроса и ответа обеспечивают согласованность, в то время как промисы позволяют легче создавать цепочки и async/await без обратных вызовов. Fetch чистый, элегантный и понятный, но существуют замечательные альтернативы, которые мы коротко рассмотрим в этой статье. 

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

  • XMLHttpRequest
  • JQuery.ajax
  • Qwest
  • SuperAgent
  • Http-client
  • Axios
  • Fetch
  • <Request> — о нём я не буду говорить, так как он устарел. 

Ниже примеры кода выполнения базовых HTTP GET и POST с использованием различных методов. 

XMLHttpRequest

Объект XMLHttpRequest можно использовать для запроса данных от веб-сервера. Это старейший метод в этом обзоре и, хотя другие варианты превосходят его, он всё ещё работоспособен и полезен благодаря своей зрелости и совместимости. 

GET

var req = new XMLHttpRequest();

//Свойство onreadystatechange
//определяет функцию, которая будет 
//выполняться каждый раз при изменении
//статуса XMLHttpRequest
req.onreadystatechange = function() {
    if (this.readyState == 4 && this.status == 200) {
       //Свойство responseText
       //возвращает текстовую строку           
       console.log(xhttp.responseText)
       //Что-то делает
    }
};

req.open("GET", "http://dataserver/users", true);
req.send();

POST

var formData = new FormData();
formData.append("name", "Murdock");
var req = new XMLHttpRequest();
req.open("POST", "http://dataserver/update");
req.send(formData);

Плюсы:

  • работает во всех браузерах;
  • является нативным браузером API;
  • нет необходимости загружать его с внешнего ресурса; 
  • обратная совместимость; 
  • зрелость и стабильность. 

Минусы:

  • неуклюжий и многословный синтаксис;
  • способствует аду обратных вызовов; 
  • нативно заменён на Fetch.

JQuery.ajax

До недавнего времени широко используемая библиотека для выполнения HTTP-запросов.

Все методы jQuery’s Ajax возвращают расширенный набор объекта XMLHTTPRequest.

GET

$.ajax({
    url: 'http://dataserver/data.json'
  }).done(function(data) {
    // ...делает что-то с данными
  }).fail(function() {
    // Обрабатывает ошибку
});

POST

$.ajax({
  type: "POST",
  url: 'http://dataserver/update',
  data: data,
  success: successCallBack,
  error: errorCallBack,
  dataType: dataType
});

Плюсы:

  • хорошая поддержка и документация;
  • конфигурируемый объект;
  • применяется во многих проектах;
  • пологая кривая обучения;
  • можно прервать запрос, потому что он возвращает объект XMLHttpRequest.

Минусы:

  • не является нативным;
  • необходимо загружать с внешнего ресурса;
  • добавлены все функции JQuery, не только необходимые для выполнения HTTP-запросов.

Qwest

Qwest — это простая библиотека ajax, основанная на промисах, поддерживающая уникальные данные XmlHttpRequest2, такие как ArrayBuffer, Blob и FormData.

GET

qwest.get('http://dataserver/data.json')
     .then(function(xhr, response) {
        // ...что-то делает с данными
     });

POST

qwest.post('http://dataserver/update', {
        firstname: 'Murdock',       
        age: 30
     })
     .then(function(xhr, response) {
        // Совершает несколько полезных действий
     })
     .catch(function(e, xhr, response) {
        // Обрабатывает ошибку
     });

Плюсы:

  • можно устанавливать лимит запроса;
  • основан на промисах.

Минусы:

  • XmlHttpRequest2 доступен не во всех браузерах;
  • не является нативным;
  • необходимо загружать с внешнего ресурса.

SuperAgent

SuperAgent — это ajax API, созданный для гибкости и читаемости, с низкой кривой обучения. Он также работает с Node.js

GET

request('GET', 'http://dataserver/data.json').then(
success, failure);

Метод .query() принимает объекты, которые при использовании с методом GET формируют строку запроса. Следующий код сформирует путь /dataserver/search?name=Manny&lastName=Peck&order=desc.

request
   .get('/dataserver/search')
   .query({ name: 'Templeton' })
   .query({ lastname: 'Peck' })
   .query({ order: 'desc' })
   .then(res => {console.dir(res)}
});

POST

request
   .post('http://dataserver/update')
   .send({ name: 'Murdock' })
   .set('Accept', 'application/json')
   .then(res => {
      console.log('result' + JSON.stringify(res.body));
   });

Плюсы:

  • основан на промисах;
  • работает и с Node.js, и с браузером;
  • чтобы прервать запрос, вызывает метод request.abort();
  • известная в сообществе библиотека;
  • целостный интерфейс для осуществления HTTP-запросов;
  • поддерживает повторные запросы при возникновении ошибки.

Минусы:

  • не поддерживает мониторинг прогресса загрузки, как XMLHttpRequest;
  • не является нативным;
  • необходимо загружать с внешнего ресурса.

Http-client

Http-client позволяет создавать HTTP-клиенты, используя Fetch API.

GET

// используя модули ES6
import { createFetch, base, accept, parse } from 'http-client'

const fetch = createFetch(
  base('http://dataserver/data.json'),  
  accept('application/json'),     
  parse('json')                      
)

fetch('http://dataserver/data.json').then(response => {
  console.log(response.jsonData)
})

POST

// используя модули ES6
import { createFetch, method, params } from 'http-client'

const fetch = createFetch(
  params({ name: 'Murdock' }),
  base('http://dataserver/update')
)

Плюсы:

  • работает и с Node.js, и с браузером;
  • используется сервисными воркерами;
  • основан на промисах;
  • обеспечивает защиту заголовка для лучшей безопасности CORS.

Минусы:

  • не является нативным;
  • необходимо загружать с внешнего ресурса.

Axios

Основанная на промисах HTTP-библиотека для выполнения HTTP-запросов и в браузере, и в Node.js.

GET

axios({
  url: 'http://dataserver/data.json',
  method: 'get'
})

POST

axios.post('http://dataserver/update', {
    name: 'Murdock'
  })
  .then(function (response) {
    console.log(response);
  })
  .catch(function (error) {
    console.log(error);
  });

Плюсы:

  • использует промисы, чтобы избежать ада обратных вызовов; 
  • работает и в браузере, и в Node.js;
  • поддерживает процесс загрузки;
  • можно устанавливать время ожидания ответа;
  • настройка запросов просто передачей объекта конфигурации; 
  • реализовал предложение об отмене промиса;
  • автоматически конвертирует данные в JSON.

Минусы:

  • не является нативным;
  • необходимо загружать с внешнего ресурса.

Fetch

Fetch является нативным API браузера для выполнения запросов, который заменяет XMLHttpRequest. Fetch позволяет выполнять сетевые запросы проще, чем XMLHttpRequest. Fetch API использует промисы, избегая ада обратных вызовов XMLHttpRequest.

GET

//С ES6 fetch
fetch('http://dataserver/data.json')
  .then(data => {
    // ...что-то делает с данными
  }).catch(error => {
    // Обработка ошибки
});

POST

fetch('http://dataserver/update', {
  method: 'post',
  headers: {
    'Accept': 'application/json, text/plain, */*',
    'Content-Type': 'application/json'
  },
  body: JSON.stringify({name: 'Murdock'})
}).then(res=>res.json())
  .then(res => console.log(res));

// Или с ES2017, например

(async () => {
  
  const response = await fetch(‘http://dataserver/update’, {
    method: ‘POST’,
    headers: {
      ‘Accept’: ‘application/json’,
      ‘Content-Type’: ‘application/json’
    },
    body: JSON.stringify({name:’Murdock’})
  });

const result = await response.json();

console.log(result);
})();

Плюсы:

  • является нативным API браузера;
  • Fetch по сути — это правильно сделанный XMLHttpRequest;
  • нет необходимости загружать его с внешнего ресурса;
  • использует промисы, чтобы избежать ада обратных вызовов;
  • не нуждается в большом количестве зависимостей;
  • дружелюбен и прост в освоении;
  • совместим с большинством наиболее часто используемых браузеров;
  • является естественной заменой нативного объекта XMLHttpRequest;
  • пологая кривая обучения.

Минусы:

  • двухэтапный процесс при обработке данных JSON: первый — выполнение запроса, второй —ответный вызов метода .json(). В Axios вы получаете JSON ответ по умолчанию;
  • промис, который возвращается от Fetch (), отклоняется только при сбое сети или в случае, если что-то помешало завершению запроса. Не отклоняет ошибку статуса HTTP даже при возврате HTTP 404 или 500;
  • отсутствие некоторых полезных функций других библиотек, например отмены запросов;
  • Fetch не отправляет и не получает куки с сервера по умолчанию, что приводит к неаутентифицированным запросам, если сайт поддерживает пользовательскую сессию. Но эту функцию можно включить, добавив: {
    credentials: “same-origin.”
    }

Заключение

Fetch — это новый стандарт, поддерживаемый последними версиями Chrome и Firefox без использования дополнительной библиотеки. 

Рекомендуется посвятить некоторое время изучению характеристик Axios, SuperAgent и других библиотек, поскольку все они имеют соответствующую документацию, просты в использовании и их кривая обучения не велика. Для некоторых задач они предлагают функции, которых нет в Fetch.

Лично я использую Fetch, потому что специальные функции мне не нужны, он нативен в JavaScript и его достаточно для моего проекта. 

Спасибо! Я надеюсь статья поможет вам разобраться с тем, как работать с асинхронным кодом в JavaScript.

Читайте также:


Перевод статьи Kesk Noren: Comparing different ways to make HTTP requests in Javascript in 2020