В этой статье мы узнаем, как выглядит новый API Fetch, какие проблемы он решает, а также наиболее практичный способ извлечения удаленных данных из вашей веб-страницы с помощью функции fetch()
.
В течение многих лет XMLHttpRequest был доверенным сторонником веб-разработчиков. XMLHttpRequest
будь то напрямую или XMLHttpRequest
, включил Ajax и целый новый тип интерактивного взаимодействия, от Gmail до Facebook.
Однако XMLHttpRequest
постепенно вытесняется API Fetch . Оба могут быть использованы для сетевых запросов, но Fetch API основан на Promise, что обеспечивает более чистый и лаконичный синтаксис и помогает вам не попадать в ад .
Fetch API
API Fetch предоставляет метод fetch()
определенный для объекта window
, который вы можете использовать для выполнения запросов. Этот метод возвращает Promise, который вы можете использовать для получения ответа на запрос.
Метод fetch
имеет только один обязательный аргумент — URL-адрес ресурса, который вы хотите получить. Очень простой пример будет выглядеть примерно так: Это получает первые пять сообщений от r / javascript на Reddit :
fetch('https://www.reddit.com/r/javascript/top/.json?limit=5') .then(res => console.log(res));
Если вы проверите ответ в консоли вашего браузера, вы должны увидеть объект Response
с несколькими свойствами:
{ body: ReadableStream bodyUsed: false headers: Headers {} ok: true redirected: false status: 200 statusText: "" type: "cors" url: "https://www.reddit.com/top/.json?count=5" }
Кажется, что запрос был успешным, но где наши пять лучших постов? Давай выясним.
Загрузка JSON
Мы не можем заблокировать пользовательский интерфейс в ожидании завершения запроса. Вот почему fetch()
возвращает Promise
, объект, который представляет будущий результат. В приведенном выше примере мы используем метод then
чтобы дождаться ответа сервера и записать его на консоль.
Теперь давайте посмотрим, как мы можем извлечь полезную нагрузку JSON из этого ответа после завершения запроса:
fetch('https://www.reddit.com/r/javascript/top/.json?limit=5') .then(res => res.json()) .then(json => console.log(json));
Мы начинаем запрос с вызова fetch()
. Когда обещание выполнено, он возвращает объект Response
, который предоставляет метод json
. В первой функции then()
мы можем вызвать этот метод json
чтобы вернуть тело ответа в виде JSON.
Тем не менее, метод json
также возвращает обещание, что означает, что нам нужно выполнить цепочку для другого then()
, прежде чем ответ JSON будет зарегистрирован на консоли.
И почему json()
возвращает обещание? Поскольку HTTP позволяет вам передавать контент на клиентский кусок за фрагментом, поэтому, даже если браузер получает ответ от сервера, тело контента может еще не быть там!
Асинхронно… жду
Синтаксис .then()
хорош, но более кратким способом обработки обещаний в 2018 году является использование async … await
— нового синтаксиса, введенного ES2017. Использование async Ú await
означает, что мы можем пометить функцию как async
, затем дождаться завершения обещания с ключевым словом await
и получить доступ к результату как к обычному объекту. Асинхронные функции поддерживаются во всех современных браузерах (кроме IE или Opera Mini) и Node.js 7.6+.
Вот как будет выглядеть приведенный выше пример (слегка расширенный) с использованием async … await
:
async function fetchTopFive(sub) { const URL = `https://www.reddit.com/r/${sub}/top/.json?limit=5`; const fetchResult = fetch(URL) const response = await fetchResult; const jsonData = await response.json(); console.log(jsonData); } fetchTopFive('javascript');
Не так много изменилось. Помимо того, что мы создали async
функцию, которой мы передаем имя subreddit, мы теперь ожидаем результата вызова fetch()
, а затем снова используем await
для получения JSON из ответа.
Это основной рабочий процесс, но дела с удаленными службами не всегда проходят гладко.
Обработка ошибок
Представьте, что мы запрашиваем у сервера несуществующий ресурс или ресурс, требующий авторизации. С fetch()
вы должны обрабатывать ошибки уровня приложения, такие как 404 ответа, внутри нормального потока. Как мы видели ранее, fetch()
возвращает объект Response
со свойством ok
. Если response.ok
имеет значение true
, код состояния ответа находится в диапазоне 200:
async function fetchTopFive(sub) { const URL = `http://httpstat.us/404`; // Will return a 404 const fetchResult = fetch(URL) const response = await fetchResult; if (response.ok) { const jsonData = await response.json(); console.log(jsonData); } else { throw Error(response.statusText); } } fetchTopFive('javascript');
Значение кода ответа от сервера варьируется от API к API, и часто проверки response.ok
может быть недостаточно. Например, некоторые API возвращают ответ 200, даже если ваш ключ API недействителен. Всегда читайте документацию по API!
Чтобы обработать сбой сети, используйте блок try … catch
:
async function fetchTopFive(sub) { const URL = `https://www.reddit.com/r/${sub}/top/.json?limit=5`; try { const fetchResult = fetch(URL) const response = await fetchResult; const jsonData = await response.json(); console.log(jsonData); } catch(e){ throw Error(e); } } fetchTopFive('javvascript'); // Notice the incorrect spelling
Код внутри блока catch
будет выполняться только при возникновении сетевой ошибки.
Вы узнали основы создания запросов и чтения ответов. Теперь давайте настроим запрос дальше.
Изменить метод запроса и заголовки
Глядя на приведенный выше пример, вы можете задаться вопросом, почему нельзя просто использовать одну из существующих оболочек XMLHttpRequest . Причина в том, что API-интерфейс fetch предлагает больше, чем просто метод fetch()
.
Хотя вы должны использовать один и тот же экземпляр XMLHttpRequest
для выполнения запроса и получения ответа, API извлечения позволяет вам явно настроить объекты запроса.
Например, если вам нужно изменить способ, которым fetch()
делает запрос (например, для настройки метода запроса), вы можете передать объект Request
в функцию fetch()
. Первый аргумент конструктора Request
— это URL-адрес запроса, а второй аргумент — объект опции, который конфигурирует запрос:
async function fetchTopFive(sub) { const URL = `https://www.reddit.com/r/${sub}/top/.json?limit=5`; try { const fetchResult = fetch( new Request(URL, { method: 'GET', cache: 'reload' }) ); const response = await fetchResult; const jsonData = await response.json(); console.log(jsonData); } catch(e){ throw Error(e); } } fetchTopFive('javascript');
Здесь мы указали метод запроса и попросили его никогда не кэшировать ответ.
Вы можете изменить заголовки запроса, назначив объект headers
полю headers
запроса. Вот как запросить контент JSON только с заголовком 'Accept'
:
const headers = new Headers(); headers.append('Accept', 'application/json'); const request = new Request(URL, { method: 'GET', cache: 'reload', headers: headers });
Вы можете создать новый запрос из старого, чтобы настроить его для другого варианта использования. Например, вы можете создать запрос POST из запроса GET к тому же ресурсу. Вот пример:
const postReq = new Request(request, { method: 'POST' });
Вы также можете получить доступ к заголовкам ответов, но имейте в виду, что они являются значениями только для чтения.
fetch(request).then(response => console.log(response.headers));
Request
и Response
строго следуют спецификации HTTP; вы должны узнать их, если вы когда-либо использовали язык на стороне сервера. Если вы хотите узнать больше, вы можете прочитать о них на странице Fetch API в MDN .
Объединяя все вместе
Чтобы завершить статью, вот работающий пример, демонстрирующий, как получить пять лучших постов из определенного подредакта и отобразить их данные в списке.
Попробуйте ввести несколько subreddits (например, «javascript», «node», «linux», «lolcats»), а также пару несуществующих.
Куда пойти отсюда
В этой статье вы увидели, как выглядит новый Fetch API, и какие проблемы он решает. Я продемонстрировал, как получать удаленные данные с помощью метода fetch()
, как обрабатывать ошибки и создавать объекты Request
для управления методом запроса и заголовками.
И, как показано на следующем рисунке, поддержка fetch()
хорошая. Если вам нужна поддержка старых браузеров, вы можете использовать полифилл .
Поэтому в следующий раз, когда вам понадобится библиотека, такая как jQuery, для выполнения запросов Ajax, подумайте, не могли бы вы вместо этого использовать собственные методы браузера.