Метод fetch() невероятно полезен для тех, кто пытается получить или изменить данные, хранящиеся на удаленном сервере (или смоделированные удаленные серверы на хост-компьютере). Основной формат любого запроса на выборку следующий:

.fetch('server-address', 'options')

С адресом сервера — это удаленный адрес, откуда мы хотели бы изменить или получить данные, и параметр, который будет указывать, как и что мы конкретно хотим сделать.

В качестве примера рассмотрим локальный сервер.

.fetch("http://localhost:3000/movies")

Без второго аргумента fetch по умолчанию будет запрашивать все данные в этом месте с сервера. В этом случае мы можем сказать, что на нашем сервере размещены изображения наших любимых фильмов с их описанием и названиями, связанными с каждым изображением.

Что, если нам нужен только один предмет? Мы можем добавить идентификатор в конец нашего запроса на выборку, чтобы вернуть определенный элемент.

.fetch("http://localhost:3000/movies/1")

даст нам элемент с идентификатором 1 в нашей базе данных (обычно первый элемент).

Этот процесс часто называют GET, так как мы получаем данные с сервера. Существуют и другие способы взаимодействия с сервером, а именно PATCH, POST и DELETE.

DELETE, пожалуй, самый простой. Мы должны указать конкретный идентификатор элемента для удаления, а затем в поле параметра мы пишем

    fetch(`http://localhost:3000/movies/1`, {method:"DELETE",})

если мы хотим удалить элемент с идентификатором 1 с сервера.

Патч также требует определенного идентификатора, но вместо удаления элемента он будет изменен. Скажем, мы хотели изменить название и описание фильма, но не трогали URL-адрес изображения. То, что мы могли бы напечатать, было бы

fetch("http://localhost:3000/movies/1", {
        method:"PATCH",
        headers: {
            "Content-Type": "application/json",
            "Accept" : "application/json"
          },
          body: JSON.stringify({
            title:`All Dogs go to Heaven`,
            description:`...hopefully`,
          }),
        })

Мы видим, что это немного сложнее, но вскоре это будет иметь смысл.

headers: {
            "Content-Type": "application/json",
            "Accept" : "application/json"
          }

Этот раздел содержит информацию для сервера о том, как принимать и отправлять данные. Content-type сообщает серверу, что мы отправляем данные JSON, а Accept сообщает серверу, что мы ожидаем возврата данных JSON.

body: JSON.stringify({
            title:`All Dogs go to Heaven`,
            description:`...hopefully`,
          }),

Этот раздел тела представляет собой данные, которые мы фактически отправляем. В данном случае мы меняем заголовок на «Все псы попадают в рай», а описание на «…надеюсь». Мы оставляем изображение как есть, просто не упоминая его. Напомним, мы уже указали идентификатор элемента на сервере, который хотели изменить.

Наконец, у нас есть POST, который очень похож на PATCH. POST создает новую запись элемента для отправки на сервер. В результате этого в адресе сервера выборки не требуется id, и все поля должны быть указаны

fetch("http://localhost:3000/movies", {
        method:"POST",
        headers: {
            "Content-Type": "application/json",
            "Accept" : "application/json"
          },
          body: JSON.stringify({
            image_url:`some_image.png`,
            description:`It really wasnt in the ruelbook`,
            title:`Airbud`,
          }),
        })

Все идентично запросу PATCH с точки зрения функции. Просто не забудьте присвоить значение каждому атрибуту, существующему для элемента в базе данных. Мы бы не хотели, например, опускать строку image_url в нашем теле.

Интересно, что по умолчанию идентификатор назначается автоматически. Однако мы можем включить идентификатор, если захотим. Мы просто модифицируем тело, чтобы оно было таким.

body: JSON.stringify({
            image_url:`some_image.png`,
            description:`It really wasnt in the ruelbook`,
            title:`Airbud`,            
            id:5,
            })

Пока ни один другой элемент в базе данных не имеет этого идентификатора, он успешно отправит элемент в базу данных с указанным идентификатором. В противном случае запрос на выборку не выполняется.

Промисы и .then()

Теперь у нас есть представление о том, как можно использовать выборку для получения и изменения данных, но как сохранить ответ? Мы показали, как получить данные, но не показали, как они сохраняются в переменную. Это немного сложно, так как fetch() на самом деле асинхронна и поэтому возвращает промис. Это означает

let data = fetch("http://localhost:3000/favorites")
console.log(data)

НЕ будет распечатывать данные JSON наших фильмов. Если бы мы хотели это сделать, нам пришлось бы использовать .then()

fetch("http://localhost:3000/movies")
.then(resp=>resp.json()).then(data=>{console.log(data)})

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

.then() можно вызывать для промисов, и основное форматирование таково:

.then(response_from_promise=>{code we want to execute})

Кроме того, .then() также возвращает обещание. это означает, что мы можем связать методы .then() для достижения желаемого результата. В приведенном выше случае мы конвертируем полученные данные в json, используя .json() в ответе, а затем console.log() данные

Обратите внимание, что это означает, что мы могли бы написать

fetch("http://localhost:3000/movies")
.then(r=>r.json()).then(d=>{console.log(d)})

и это сделает то же самое. Переменная, которую мы поместили в начало .then(), просто соответствует результатам предыдущего промиса, то есть мы можем назвать эту переменную произвольно.

Мы могли бы использовать .then() для присвоения значения переменной, но это сопряжено со своими проблемами. Предположим, что запись 1 в нашей базе данных имеет атрибут заголовка «новый».

let mainTitle = "old"
fetch("http://localhost:3000/movies/1")
.then(r=>r.json()).then(d=>{mainTitle = d.title; 
console.log(mainTitle);})

console.log(mainTitle)

Вероятно, это приведет к

>old
>new

печатаются в таком порядке. Обратите внимание, что old печатается первым, несмотря на то, что мы изменили его значение в более ранней строке, чем в console.log(). Это связано с тем, что, как обсуждалось ранее, fetch() является асинхронным вызовом.

Это означает, что он удаляется из обычного порядка выполнения и вместо этого выполняется одновременно с остальной частью кода. Это означает, что console.log() в последней строке может выполняться до переназначения mainTitle, что позволяет распечатать «старый»

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

На этом основы .fetch() и .then() завершаются. Ниже у нас есть несколько советов и распространенных ошибок

Распространенные ошибки

Во время этого процесса можно совершить довольно много ошибок. Я поделюсь тем, что было наиболее частым для меня во время обучения

  1. Забыть функцию окружения JSON.stringify() для тела. Это может вас сбить с толку, так как есть два способа записать значение для тела. Мы можем использовать JSON.stringify() или написать его напрямую. разница в том, что синтаксис у них разный, поэтому запись значения так, как будто оно должно быть в JSON.stringify(), но не вставленное, или наоборот, вызовет проблемы и может привести к сбою выборки.
  2. Забыв, в конце строк в опциях fetch(). В отличие от английского, список аргументов, которые мы предоставляем как для JSON.stringify(), так и для опции в целом, не пропускает , в последнем элементе. Например, верхний правильный, а нижний неправильный. Разница всего две запятые
    fetch("http://localhost:3000/favorites", {
        method:"POST",
        headers: {
            "Content-Type": "application/json",
            "Accept" : "application/json"
          },
          body: JSON.stringify({
            image_url:`${rImage}`,
            description:`${rDescription}`,
            title:`${rTitle}`,
            date:`${rDate}`, 
            id:setId,
            keywords: rKeywords,
          }),
        })
    return fetch("http://localhost:3000/favorites", {
        method:"POST",
        headers: {
            "Content-Type": "application/json",
            "Accept" : "application/json"
          },
          body: JSON.stringify({
            image_url:`${rImage}`,
            description:`${rDescription}`,
            title:`${rTitle}`,
            date:`${rDate}`, 
            id:setId,
            keywords: rKeywords
          })
        })

3. Обработка промиса как переменной. Помнить. мы не можем использовать console.log(fetch()). Мы должны использовать fetch().then(d=›console.log(d)) для полного ответа или fetch().then(resp=›resp.json()).then(d=›console.log( г)) только для печати данных

4. Не забывайте учитывать асинхронность выборки. Мы не можем рассчитывать на то, что переменные, обновленные с помощью fetch, будут обновлены вовремя, если эти переменные используются вне .fetch() или .then().

Советы

  1. Мы можем обновлять переменные с помощью .fetch(), у нас был один такой пример выше
fetch("http://localhost:3000/movies/1")
.then(r=>r.json()).then(d=>{mainTitle = d.title)

Это можно использовать в приложении, которое отображает изображения и названия фильмов. Затем это обновит заголовок, сохраненный на стороне клиента. В зависимости от реализации, это может быть все, что нам нужно сделать. Или нам может понадобиться запустить какую-то функцию после того, как мы изменим значение mainTitle, что мы могли бы сделать в другом операторе .then(), присоединенном к концу последнего оператора .then().

2. Исходя из личного опыта, одна из самых сложных вещей, о которой вам, возможно, придется подумать, это то, как мы узнаем идентификатор записи в базе данных, которую мы хотим удалить или исправить. Это становится особенно актуальным, если мы используем базу данных для загрузки большого количества изображений на стороне клиента, а затем хотим, чтобы клиент мог изменять/удалять изображения, щелкая изображения или выполняя другие действия. Один, длинный способ сделать это — снова просмотреть базу данных для любых записей, которые имеют тот же URL-адрес изображения, что и цель пользователя изображения (image.src). В качестве альтернативы мы можем каким-то образом встроить информацию об идентификаторе в элемент html, представляющий изображение. Я рекомендую сделать так, чтобы идентификатор элемента содержал его собственный идентификатор в базе данных.

<img src="https://images-assets.nasa.gov/image/GSFC_20171208_Archive_e000148/GSFC_20171208_Archive_e000148~thumb.jpg" 
id="im_7" class="fav-images">

В этом примере элемент html имеет идентификатор «im_7». Мы можем сказать, что сделали это, добавив идентификатор в базе данных к строке «im_». Это позволяет нам снова извлечь идентификатор, взяв подмножество строки идентификатора html, и легко использовать эту информацию, чтобы затем указать (ИСПРАВИТЬ или УДАЛИТЬ) соответствующую запись в базе данных.

3. Мы также можем возвращать промисы из функций. Если мы это сделаем, то сможем ссылаться на этот вызов функции как на сам промис.

function getIt(){
    return fetch("http://localhost:3000/favorites")
}

getIt().then(resp=>resp.json())(data=>{console.log(data)})

Источники:

Выбрать (javascript.info)

fetch() — веб-API | MDN (mozilla.org)

Параметры заголовка: «Accept и Content-type в контексте REST — Stack Overflow на русском

html — Что означает «Принять: */* в разделе Клиент заголовков запроса? - Переполнение стека"