# Получение данных от сервера с помощью fetch

poster
В этом уроке мы с вами разберем что такое fetch и как он отличается от XMLHttpRequest.
Понравилось? Поделитесь с друзьями!
Понравилось?
Поделитесь с друзьями!
Комментарии
Текст видео

В этом уроке мы с вами разберем что такое fetch и как он отличается от XMLHttpRequest. Fetch - это улучшеный XMLHttpRequest, который по умолчанию использует промисы и более простое и чистое API.

Следует сразу заметить, что fetch не поддерживается всеми браузерами. Например в IE 10-11 он не работает. В Safari он работает с версии 10. Конечно для него есть полифил, который использует XMLHttpRequest с старых браузерах.

Для того, чтобы обращатся на сервер за данными, давайте создадим API с тестовыми данными с помощью сервиса mocky.io.

Вот у меня есть JSON данных

[
  {
    "userId": 1,
    "id": 1,
    "title": "sunt aut facere repellat provident occaecati excepturi optio reprehenderit",
    "body": "quia et suscipit\nsuscipit recusandae consequuntur expedita et cum\nreprehenderit molestiae ut ut quas totam\nnostrum rerum est autem sunt rem eveniet architecto"
  },
  {
    "userId": 1,
    "id": 2,
    "title": "qui est esse",
    "body": "est rerum tempore vitae\nsequi sint nihil reprehenderit dolor beatae ea dolores neque\nfugiat blanditiis voluptate porro vel nihil molestiae ut reiciendis\nqui aperiam non debitis possimus qui neque nisi nulla"
  },
  {
    "userId": 1,
    "id": 3,
    "title": "ea molestias quasi exercitationem repellat qui ipsa sit aut",
    "body": "et iusto sed quo iure\nvoluptatem occaecati omnis eligendi aut ad\nvoluptatem doloribus vel accusantium quis pariatur\nmolestiae porro eius odio et labore et velit aut"
  }
]

Вставляем его в body запроса и в advance mode выбираем

 Access-Control-Allow-Origin:*

Для того, чтобы любой домен мог обращатся к этому API.

Нажимаем Generate response и получаем ссылку на наш API.

Теперь давайте напишем запрос с помощью fetch, который позволит нам получить данные.

fetch('http://www.mocky.io/v2/5944e07213000038025b6f30')
  .then(function (response) {
    console.log('response')
  })

Один из нюансов в fetch, что мы не получаем сразу в response данные, а Stream-обьект. Это значит, что мы должны наш response парсить, для того, чтобы получить данные.

fetch('http://www.mocky.io/v2/5944e07213000038025b6f30')
  .then(function (response) {
    response.json().then(function (data) {
      console.log('data', data)
    })
  })

Здесь мы используем метод .json, чтобы парсить ответ как json. Результат вызова .json тоже является промисом, поэтому мы должны писать then, в котором мы получим результат. Если мы посмотрим в браузер, то у нас в консоли вывелись данные.

Этот код можно написать чуть чище, разбив на 2 then конструкции и избежав глубокой вложенности

fetch('http://www.mocky.io/v2/5944e07213000038025b6f30')
  .then(function (response) {
    return response.json()
  })
  .then(function (data) {
    console.log('data', data)
  })

Если мы посмотрим в браузер, то наш код работает точно также.

В получении данных часто возникают ошибки и их нужно как-то обрабатывать. В fetch с этим есть нюансы. Обычно думаешь, что если написать .catch, то отловишь любую ошибку.

fetch('http://www.mocky.io/v2/5944e07213000038025b6f30')
  .then(function (response) {
    return response.json()
  })
  .then(function (data) {
    console.log('data', data)
  })
  .catch(function (error) {
    console.log('error', error)
  })

Мы с вами добавили catch, но с спецификации fetch сказано, что туда попадают только network ошибки. То есть связанные с сетью. Например, когда запрос отваливается по таймауту.

Если мы хотим отловить обычные ошибки, их нужно обрабатывать в первом блоке .then. И так как мы работаем с промисами, то мы можем их резолвить и реджектить когда хотим

fetch('http://www.mocky.io/v2/5944e07213000038025b6f30')
  .then(function (response) {
    if (response.status !== 200) {
      return Promise.reject(new Error(response.statusText))
    }
    return Promise.resolve(response)
  })
  .then(function (response) {
    return response.json()
  })
  .then(function (data) {
    console.log('data', data)
  })
  .catch(function (error) {
    console.log('error', error)
  })

Мы добавили еще один блок с resolve и reject, который позволяет сделать нашу работу с fetch правильной. Ни один из следующих then не выполнится при ошибке, а мы сразу попадем в catch.

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

var status = function (response) {
  if (response.status !== 200) {
    return Promise.reject(new Error(response.statusText))
  }
  return Promise.resolve(response)
}
var json = function (response) {
  return response.json()
}

fetch('http://www.mocky.io/v2/5944e07213000038025b6f30')
  .then(status)
  .then(json)
  .then(function (data) {
    console.log('data', data)
  })
  .catch(function (error) {
    console.log('error', error)
  })

Если например бы хотим сделать POST запрос, то там достаточно указать method post. Также мы можем добавить body, которое мы хотим передать в запросе.

var status = function (response) {
  if (response.status !== 200) {
    return Promise.reject(new Error(response.statusText))
  }
  return Promise.resolve(response)
}
var json = function (response) {
  return response.json()
}

fetch('http://www.mocky.io/v2/5944e07213000038025b6f30', {
  method: 'post',
  body: 'test=1'
})
  .then(status)
  .then(json)
  .then(function (data) {
    console.log('data', data)
  })
  .catch(function (error) {
    console.log('error', error)
  })

Как мы видим, fetch намного более гибкий чем XMLHttpRequest. Из-за использования промисов он позволяем нам комбинировать код, как нам нужно.

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

  1. Кода все равно получается много и приходится писать свою обертку вокруг fetch, чтобы было удобно его использовать
  2. Парсинг данных не особо полезен, если я получаю от сервера всегда JSON, а код приходится писать
  3. Если вы хотите передавать данные в body в виде обьекта, то вам нужно на обьекте вызывать JSON.stringify, что неудобно
  4. Обработка ошибок в fetch, когда не все ошибки падают в catch лично мне не нравится. Для меня логично что все ошибки всегда падают в catch

Есть ли у меня решение на эти вопросы? Я предпочитаю использовать библиотеки superagent или axios, где все эти вопросы уже решены и нужно писать мимимум кода. Поэтому если у вас есть выбор, какую технологию использовать, то попробуйте эти библиотеки.

Если у вас возникли какие-то вопросы или комментарии, пишите их прямо под этим видео.

Только зарегистрированные пользователи могут оставлять комментарии.  Войдите, пожалуйста.
Артур Владимирович
9 лет назад
Сейчас идёт тренд на оптимизацию загрузки, уменьшения веса бандла и отказ от сторонних библиотек, например от lodash. Я использовал для vue.js axios, но насколько будет выйгрыш или проигрыш использования fetch+полифил vs axios?
monsterlessons
9 лет назад
Мне кажется, что это опять хайп. Более быстрая разработка всегда важнее, чем скорость загрузки или передачи данных. Поэтому мы имеем микросервисы, которые увеличивают скорость передачи данных, но упрощают разработку. Тоже самое с lodash/fetch/babel. Момент, когда нужно уменьшать размер бандла на успешном проекте, я бы делал в последнюю очередь.
Zato Prosto
9 лет назад
Обязательно ли использовать промисы при обработке ответов сервера !== 200 ? Как передавать в catch данные об ошибках, не используя промисы?
monsterlessons
9 лет назад
Не обязательно. Вы можете просто бросать ошибку вот так: function handleErrors(response) { if (!response.ok) { throw Error(response.statusText); } return response; } fetch("http://httpstat.us/500") .then(handleErrors) .then(function(response) { console.log("ok"); }).catch(function(error) { console.log(error); });
Aleksandr Polyakh
9 лет назад
Доброе время суток. Если у Вас есть время запишите,что-то с применением библиотеки https://github.com/mzabriskie/axios. В приложении приближенного к реальному. Спасибо!
monsterlessons
9 лет назад
Добрый день. Спасибо за идею. Добавил в список будущих видео.
Андрей
9 лет назад
Хочу еще добавить немаловажный на мой взгляд момент. Бывает необходимо сделать запрос в в какой-то закрытый API, или выполнить другое действие, связанное с сессиями или авторизацией. И неработоспособность в такой ситуации метода fetch может серьезно озадачить. Это связано с тем, что по умолчанию fetch не передает куки в запросе. Чтобы это исправить, необходимо в запросе передать параметр: credentials: 'same-origin'. Примерно так: ``` fetch(`/api/user/${id}`, { method: 'DELETE', credentials: 'same-origin' } ```
monsterlessons
9 лет назад
Согласен на 100%. Забыл рассказать это в видео.