Promise и AJAX в JavaScript: первый запрос через fetch

Рано или поздно JavaScript перестает жить только внутри страницы. Хочется нажать кнопку и получить данные с сервера: список постов, цену товара, погоду, курс валют, статус заказа

Раньше такие запросы часто называли AJAX. Сейчас в обычном браузерном JavaScript для этого чаще используют fetch. А fetch возвращает Promise, потому что ответ от сервера приходит не мгновенно

В этом уроке сделаем первый запрос к API, покажем loading, обработаем ошибку и выведем данные на страницу

Что получится в конце

Мы соберем кнопку "Загрузить пост" и блок результата:

fetch("https://jsonplaceholder.typicode.com/posts/1")
  .then((response) => response.json())
  .then((post) => {
    console.log(post.title);
  });

Потом перепишем на async/await, потому что так код часто читается проще

Что такое Promise простыми словами

Promise — это объект, который представляет будущий результат асинхронной операции

Состояния:

  • pending — операция еще идет;
  • fulfilled — операция завершилась успешно;
  • rejected — операция завершилась ошибкой.

MDN описывает Promise как объект, который представляет eventual completion или failure асинхронной операции и ее значение. Для практики запомним проще: Promise — это обещание результата позже

Пример без сети:

const promise = new Promise((resolve) => {
  setTimeout(() => {
    resolve("Данные готовы");
  }, 1000);
});

promise.then((message) => {
  console.log(message);
});

Код не блокирует страницу. Он говорит: когда данные будут готовы, выполни функцию в then

Что делает fetch

fetch() делает сетевой запрос. По документации Fetch API метод fetch доступен в браузерном контексте и возвращает Promise с объектом Response

Минимальный пример:

fetch("https://jsonplaceholder.typicode.com/posts/1")
  .then((response) => response.json())
  .then((post) => {
    console.log(post);
  });

Здесь два асинхронных шага:

  1. Дождаться ответа сервера.
  2. Прочитать тело ответа как JSON.

Поэтому response.json() тоже возвращает Promise

Делаем HTML

<button class="load-post">Загрузить пост</button>
<p class="status">Нажмите кнопку</p>
<article class="post"></article>

Первый fetch по клику

const loadButton = document.querySelector(".load-post");
const status = document.querySelector(".status");
const postContainer = document.querySelector(".post");

loadButton.addEventListener("click", () => {
  status.textContent = "Загружаем...";

  fetch("https://jsonplaceholder.typicode.com/posts/1")
    .then((response) => response.json())
    .then((post) => {
      status.textContent = "Готово";
      postContainer.innerHTML = `
        <h2>${post.title}</h2>
        <p>${post.body}</p>
      `;
    });
});

Теперь кнопка делает запрос и выводит данные

Почему HTTP-ошибка не всегда попадает в catch

У fetch есть важный нюанс: если сервер ответил статусом 404 или 500, сам Promise может считаться выполненным, потому что ответ от сервера получен. Нужно проверять response.ok

fetch("https://jsonplaceholder.typicode.com/posts/1")
  .then((response) => {
    if (!response.ok) {
      throw new Error("Ошибка сервера");
    }

    return response.json();
  })
  .then((post) => {
    console.log(post);
  })
  .catch((error) => {
    console.log(error.message);
  });

catch поймает сетевую ошибку или ошибку, которую мы сами выбросили через throw

Версия с обработкой ошибки на странице

loadButton.addEventListener("click", () => {
  status.textContent = "Загружаем...";
  postContainer.innerHTML = "";

  fetch("https://jsonplaceholder.typicode.com/posts/1")
    .then((response) => {
      if (!response.ok) {
        throw new Error("Сервер вернул ошибку");
      }

      return response.json();
    })
    .then((post) => {
      status.textContent = "Готово";
      postContainer.innerHTML = `
        <h2>${post.title}</h2>
        <p>${post.body}</p>
      `;
    })
    .catch((error) => {
      status.textContent = "Не получилось загрузить данные";
      postContainer.textContent = error.message;
    });
});

Это уже нормальная схема для интерфейса:

  • перед запросом показываем loading;
  • при успехе выводим данные;
  • при ошибке показываем понятное сообщение.

async/await

Тот же код можно написать так:

loadButton.addEventListener("click", async () => {
  status.textContent = "Загружаем...";
  postContainer.innerHTML = "";

  try {
    const response = await fetch("https://jsonplaceholder.typicode.com/posts/1");

    if (!response.ok) {
      throw new Error("Сервер вернул ошибку");
    }

    const post = await response.json();

    status.textContent = "Готово";
    postContainer.innerHTML = `
      <h2>${post.title}</h2>
      <p>${post.body}</p>
    `;
  } catch (error) {
    status.textContent = "Не получилось загрузить данные";
    postContainer.textContent = error.message;
  }
});

await можно читать как "подожди результат Promise". Но использовать его можно внутри async-функции

AJAX и fetch — это одно и то же?

Не совсем. AJAX — более старое общее название подхода: страница общается с сервером без полной перезагрузки. Раньше часто использовали XMLHttpRequest. Сейчас для многих задач используют fetch

Если пользователь ищет javascript promise ajax example, ему обычно нужен современный пример: кнопка, запрос, JSON, loading, ошибка. Поэтому fetch — хороший вход

CORS: почему запрос может быть заблокирован

Иногда код правильный, но браузер пишет ошибку CORS. Это значит, что сервер не разрешил странице с твоего домена читать ответ

Примерно:

Access to fetch at ... has been blocked by CORS policy

Это не лечится простым изменением JavaScript на клиенте. Нужно:

  • использовать API, которое разрешает такие запросы;
  • настроить CORS на своем сервере;
  • делать запрос через свой backend;
  • не пытаться читать чужой сайт как API.

Частые ошибки

Забыли return response.json()

Неправильно:

fetch(url)
  .then((response) => {
    response.json();
  })
  .then((data) => {
    console.log(data);
  });

Во второй then придет undefined

Правильно:

fetch(url)
  .then((response) => response.json())
  .then((data) => {
    console.log(data);
  });

Не обработали ошибку

Без catch пользователь может просто увидеть тишину. В интерфейсе всегда лучше показать понятное состояние

Думают, что fetch сразу возвращает данные

const data = fetch(url);

data будет Promise, а не готовые данные. Нужен then или await

Используют await не внутри async

const response = await fetch(url);

Так можно только внутри async-функции или в окружении, где разрешен top-level await

Мини-задание

Сделай загрузку списка постов:

const response = await fetch("https://jsonplaceholder.typicode.com/posts?_limit=5");
const posts = await response.json();

Выведи каждый пост:

postContainer.innerHTML = posts
  .map((post) => `<h2>${post.title}</h2><p>${post.body}</p>`)
  .join("");

Добавь:

  • состояние "Загружаем…";
  • обработку ошибки;
  • очистку старого результата перед новым запросом.

Ответы на эти вопросы могут быть для вас полезными

Что такое Promise в JavaScript?

Это объект, который представляет будущий результат асинхронной операции: успешный ответ или ошибку

Что такое AJAX?

Это подход, когда страница получает данные с сервера без полной перезагрузки. Сейчас часто используют fetch

Что возвращает fetch?

fetch возвращает Promise с объектом Response. Чтобы получить JSON, нужно вызвать response.json()

Почему fetch не попал в catch при 404?

Потому что HTTP-ответ был получен. Нужно вручную проверить response.ok и выбросить ошибку, если статус плохой

Что лучше: then или async/await?

Оба варианта рабочие. async/await часто легче читать, но полезно понимать и цепочку then

Что почитать дальше по JavaScript

Чтобы двигаться по теме без рывков, рядом лучше открыть:

Оцените статью
0 0 голоса
Рейтинг статьи
Подписаться
Уведомить о
guest

0 комментариев
Старые
Новые Популярные
Межтекстовые Отзывы
Посмотреть все комментарии
0
Оставьте комментарий! Напишите, что думаете по поводу статьи.x