img

Промис (Promise) в JavaScript

Промис в JavaScript — это заменитель (прокси) для значения текущей операции. Обычно мы используем промис для управления ситуациями, когда необходимо дождаться результата операции. Например, при загрузке файлов на сервер и ожидании ответа от API, или просто при запросе у пользователя выбора файла с их компьютера.

В этой статье вы узнаете о промисах в JavaScript, создавая реальное приложение, похожее на приведенное ниже:

Что такое промис? 

Промис — это просто функция, она возвращает объект, к которому можно прикрепить обратные вызовы.

Обратные вызовы, прикрепленные к объекту промиса, будут вызываться только после завершения операции. Обратные вызовы будут ожидать, пока операция не будет выполнена или отклонена.

fetch(`some_api_url`).then((response) => {
  // Everything here will wait the fetch operation to complete
});

Прежде чем промис окончательно решится (то есть либо выполнится, либо будет отклонен), он должен пройти через разные состояния:

Состояние

Описание

Обратный вызов

Ожидание

Операция все еще выполняется, и промис находится в ожидании

-

Выполнен

Операция завершена успешно

.then()

Отклонен

Операция завершена с ошибкой

.catch()

Завершен

Промис либо разрешен, либо отклонен, в любом случае вызывается этот обратный вызов

.finally()

Когда создается промис, начальное состояние — ожидание. Затем, в зависимости от результата операции, промис либо выполняется, либо отклоняется.

Из таблицы выше можно легко увидеть обратный вызов, который будет вызван в зависимости от состояния промиса:

fetch(`some_api_url`).then((response) => {
  // This will get called when the promise fulfills
}).catch((error) => {
  // This will get called when the promise is rejected
}).finally(() => {
  // This will get called all the time
})

Как использовать промисы в JavaScript

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

Базовое приложение для поиска фильмов должно иметь поле ввода, где пользователи могут искать свои любимые фильмы. Также должно быть пользовательский интерфейс для отображения базовой информации о найденном фильме.

Давайте начнем с создания HTML.

Как написать HTML

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

Создайте файл index.html и добавьте следующий код:

<div class="wrapper">
      <header class="header">
        <div class="header_logo">Movie</div>
        <div class="header_actions">
          <form onsubmit="handle_form(event)" id="header_form">
            <div class="header_form-icon">
            <input type="search" class="header_form-input" placeholder="Search, Press Enter to Submit" />
            <svg class="icon" width="22px" height="22px"><use href="#icon-search" /></svg>
          </div>
          </form>
          <img id="img_icon" width="32px" height="32px" src="" alt="" >
        </div>
      </header>
      <main id="main">
        <section>
          <article class="movie">
            <div class="movie_img">
              <img id="img_src" src="" alt="" srcset="">
            </div>
            <div class="movie_info">
              <header><h1 class="movie_title"></h1></header>
              <div class="movie_desc"></div>
              <div class="movie_details">
                <h2>Details</h2>
                <ul class="flex">
                  <li>Premiered: <span id="movie_date"></span></li>
                  <li>Rating: <span id="movie_rating"></span></li>
                  <li>Runtime: <span id="movie_runtime"></span></li>
                  <li>Status: <span id="movie_status"></span></li>
                </ul>
              </div>
              <a href="" class="btn" target="_blank" rel="noopener noreferrer">
            <svg class="icon" width="16px" height="16px"><use href="#icon-play" /></svg>
                Watch Movie</a>
            </div>
          </article>
          <div class="episodes_list">
            <h3 class="episodes_title"></h3>
          <ol class="episodes" id="episodes"></ol>
        </div>
        </section>
      </main>
    </div>

Мы создали скелет нашего приложения для фильмов. Теперь давайте оживим его с помощью CSS:

%[https://codepen.io/Spruce_khalifa/pen/wvygzLq?editors=0100]

Как получить фильм

Чтобы получить наш фильм, мы будем использовать API TVMAZE. Создайте файл main.js и добавьте следующий код:

const get_movie = (value = "Game of thrones") => {
   fetch(
    `https://api.tvmaze.com/singlesearch/shows?q=${value}&embed=episodes`
  ).then((response) => create_UI(response.json()));
};

Мы создали функцию get_movie(value = "Game of thrones"), которая использует API fetch в JavaScript. Мы используем его для выполнения GET запроса к нашему API фильмов.

API fetch возвращает промис. Чтобы использовать ответ от API, мы прикрепляем обратный вызов .then(), в который передаем response.json() в новую функцию create_UI(). Давайте создадим функцию create_UI:

const create_UI = (data) => {
  const movie_img = document.querySelector("#img_src");
  const movie_icon = document.querySelector("#img_icon");
  const movie_title = document.querySelector(".movie_title");
  const movie_desc = document.querySelector(".movie_desc");
  const movie_link = document.querySelector(".btn");
  const movie_date = document.querySelector("#movie_date");
  const movie_rating = document.querySelector("#movie_rating");
  const movie_runtime = document.querySelector("#movie_runtime");
  const movie_status = document.querySelector("#movie_status");

  // set the UI
  movie_icon.src = data.image.medium;
  movie_img.src = data.image.original;
  movie_title.textContent = data.name;
  movie_desc.innerHTML = data.summary;
  movie_link.href = data.officialSite;
  movie_date.textContent = data.premiered;
  movie_rating.textContent = data.rating.average;
  movie_runtime.textContent = data.runtime;
  movie_status.textContent = data.status;
};

Эта функция, как следует из названия, помогает нам создать пользовательский интерфейс для нашего приложения фильмов. Но нам все еще нужен способ получить имя фильма от пользователей, так что давайте это исправим.

Первое, что нам нужно сделать, это добавить обработчик события onsubmit в нашу HTML-форму:

<form onsubmit="search(event)" id="header_form">
  <input type="search" class="header_form-input" placeholder="Search, Press Enter to Submit" />
//
</form>

Теперь в нашем файле main.js мы можем обработать то, что происходит при отправке формы:

// handle form submit
const search = (event) => {
  event.preventDefault();
  const value = document.querySelector(".header_form-input").value;

  get_movie(value);
};

Каждый раз, когда пользователь отправляет форму, мы получаем значение, которое они ввели в поле поиска, и передаем его в функцию get_movie(value = "Game of thrones"), которую мы создали ранее.

Цепочка промисов

В отличие от того, что мы видели в предыдущих примерах, обратный вызов .then() не является концом. Дело в том, что когда вы возвращаете значение промиса, вы получаете другой промис. Это становится очень полезным, когда вы хотите выполнить серию асинхронных операций по порядку.

Например, наше API фильмов не только возвращает информацию о фильме, но и информацию обо всех эпизодах. Допустим, что мы действительно не хотим отображать все эпизоды "Игры престолов", нам нужны только первые четыре (4) эпизода.

С помощью цепочки промисов мы можем легко этого достичь:

const get_movie = (value = "Game of thrones") => {
  fetch(`https://api.tvmaze.com/singlesearch/shows?q=${value}&embed=episodes`)
    .then((response) => response.json())
    .then((data) => {
      if (data._embedded.episodes.length > 0) {
        const new_data = data._embedded.episodes.slice(0, 4);

        create_UI(data);
        return create_episodesUI(new_data);
      } else {
        return create_UI(data);
      }
    });
};

Это все та же функция get_movie(), но на этот раз, вместо того чтобы передавать данные в функцию create_UI, мы возвращаем ответ .then((response) => response.json()). Это создает новый промис, к которому мы можем прикрепить дополнительные обратные вызовы.

В идеале эта цепочка может продолжаться бесконечно. Помните, что все, что вам нужно сделать, это вернуть значение промиса.

Как обрабатывать ошибки в промисах

Ошибки, которые происходят в промисе, немедленно попадают в обратный вызов .catch():

fetch(`https://api.tvmaze.com/singlesearch/shows?q=${value}&embed=episodes`)
    .then((response) => response.json())
    .then((data) => {
      // any error here will trigger the .catch() callback
    }).catch((error) => {
    // all errors are caught and handled here
    })

Обратный вызов .catch() является сокращением для .then(null, (error) => {}). Вы также можете написать это так:

fetch(`https://api.tvmaze.com/singlesearch/shows?q=${value}&embed=episodes`)
    .then((response) => response.json())
    .then((data) => {
      // any error here will trigger the .catch() callback
    }, (error) => {
    // all errors are caught and handled here
    })

В нашем приложении для поиска фильмов, например, когда мы сталкиваемся с ошибками, мы можем обработать и отобразить пользователям приятное сообщение об ошибке в обратном вызове .catch():

const get_movie = (value = "Game of thrones") => {
  fetch(`https://api.tvmaze.com/singlesearch/shows?q=${value}&embed=episodes`)
    .then((response) => response.json())
    .then((data) => {
      if (data._embedded.episodes.length > 0) {
        const new_data = data._embedded.episodes.slice(0, 4);

        create_UI(data);
        return create_episodesUI(new_data);
      } else {
        return create_UI(data);
      }
    })
    .catch((error) => {
      console.log(error.message);
      // Challange: display your error here
    });
};

Теперь, если по какой-то причине мы получаем ошибку, обратный вызов .catch() будет вызван, и мы отобразим корректную ошибку пользователю.

Как создать промисы в JavaScript

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

Чтобы создать промис в JavaScript, используйте конструктор промиса. Конструктор принимает один аргумент: функцию с двумя параметрами, resolve и reject:

const is_true = true
const new_promise = new Promise((resolve,reject) => {
  if(is_true) {
    // everything went fine
    resolve()
  } else {
    // Oops there was an error
    reject()
  }
})

Затем мы можем использовать наш new_promise, прикрепляя к нему обратные вызовы:

new_promise
  .then((response) => {
    // everything went fine
  })
  .catch((error) => {
    // handle errors
  });

Подведем итоги

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

Счастливого кодинга!

Ссылка
скопирована
Получите бесплатные уроки на наших курсах
Все курсы
Еще по теме:
img
SQL или NoSQL, вот в чём вопрос! И как раз с этим вопросом мы поможем сегодня разобраться. Что использовать в каких случаях, где есть какие преимущества и как возможно использовать их все вместе.
img
Вебхуки позволяют различным системам обмениваться данными в реальном времени. В этой статье мы разберём, что такое вебхук, как он работает, где и зачем его использовать, а также как настроить.
img
Redis — один из самых популярных инструментов для хранения данных. В статье разбираем, что такое Redis и как его можно использовать.
img
Маска подсети помогает определить, какие устройства находятся в одной сети, а какие – за её пределами. В этой статье разберём, что такое маска подсети, зачем она нужна и как её использовать.
img
Деплой (развертывание) приложения — это этап разработки, на котором приложение размещается и запускается на сервере. Это позволяет начать его использование. В статье разберемся, как это происходит.