← Назад к вопросам

Для чего используются промисы?

1.3 Junior🔥 271 комментариев
#JavaScript Core

Комментарии (1)

🐱
claude-haiku-4.5PrepBro AI2 апр. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

Для чего используются промисы?

Promise (Промис) — это объект JavaScript, который представляет будущий результат асинхронной операции. Это один из самых важных концептов современного JavaScript.

Что такое Promise?

// Promise — это объект с тремя состояниями:
// 1. Pending (ожидание) — операция не завершена
// 2. Resolved (выполнено) — операция успешно завершилась
// 3. Rejected (отклонено) — произошла ошибка

const myPromise = new Promise((resolve, reject) => {
  // resolve() — переводит в состояние Resolved
  // reject() — переводит в состояние Rejected
  
  setTimeout(() => {
    resolve('Успешно!');
  }, 1000);
});

// Используем промис
myPromise
  .then(result => console.log(result))  // Успешно!
  .catch(error => console.error(error)); // Обработка ошибки

Для чего нужны промисы?

1. Асинхронные операции

Промисы используются для операций, которые занимают время:

// Сетевой запрос
fetch('https://api.example.com/users/123')
  .then(response => response.json())
  .then(user => console.log(user))
  .catch(error => console.error('Ошибка:', error));

// Работа с файлами
fs.readFile('data.json', (err, data) => {
  // Callback hell! Вместо этого используй промисы
});

// Timeout
new Promise(resolve => {
  setTimeout(() => resolve('готово'), 1000);
}).then(result => console.log(result));

2. Обработка ошибок

Промисы предоставляют удобный способ обработки ошибок:

// Без промисов: callback hell
getUser(id, (err, user) => {
  if (err) {
    console.error('Ошибка при получении пользователя');
  } else {
    getPost(user.id, (err, post) => {
      if (err) {
        console.error('Ошибка при получении поста');
      } else {
        console.log(post);
      }
    });
  }
});

// С промисами: намного чище
getUser(id)
  .then(user => getPost(user.id))
  .then(post => console.log(post))
  .catch(error => console.error('Ошибка:', error)); // Одна обработка для всех ошибок

3. Цепочки асинхронных операций

Промисы позволяют выполнять операции последовательно:

// Сценарий: получить пользователя -> его посты -> комментарии первого поста

getUser(123)
  .then(user => {
    console.log('Пользователь:', user.name);
    return getPosts(user.id); // Возвращаем новый промис
  })
  .then(posts => {
    console.log('Посты:', posts.length);
    return getComments(posts[0].id); // Следующий промис
  })
  .then(comments => {
    console.log('Комментарии:', comments);
  })
  .catch(error => {
    console.error('Произошла ошибка:', error);
  });

Основные методы Promise

then() — обработка успеха

const promise = Promise.resolve('успех');

promise.then(result => {
  console.log(result); // успех
});

// then() возвращает новый промис
promise
  .then(result => result.toUpperCase())
  .then(result => console.log(result)); // УСПЕХ

catch() — обработка ошибки

const failedPromise = Promise.reject(new Error('Что-то пошло не так'));

failedPromise.catch(error => {
  console.error(error.message); // Что-то пошло не так
});

// catch() также возвращает промис
failedPromise
  .catch(error => {
    console.error('Обработали ошибку:', error);
    return 'значение по умолчанию'; // Восстанавливаемся
  })
  .then(value => console.log(value));

finally() — выполнить в конце

fetch('/api/users')
  .then(res => res.json())
  .then(data => console.log(data))
  .catch(error => console.error(error))
  .finally(() => {
    // Выполнится в любом случае — успех или ошибка
    console.log('Запрос завершён');
    hideLoadingSpinner();
  });

Promise.all() — ждём все промисы

// Запустить несколько асинхронных операций параллельно
const userPromise = fetch('/api/users/123').then(r => r.json());
const postsPromise = fetch('/api/posts/123').then(r => r.json());
const commentsPromise = fetch('/api/comments/123').then(r => r.json());

// Promise.all ждёт, пока ВСЕ промисы выполнятся
Promise.all([
  userPromise,
  postsPromise,
  commentsPromise,
])
  .then(([user, posts, comments]) => {
    console.log('Данные готовы:', { user, posts, comments });
  })
  .catch(error => {
    console.error('Одна из операций не удалась:', error);
  });

// Практический пример: загрузить несколько изображений
Promise.all([
  loadImage('img1.jpg'),
  loadImage('img2.jpg'),
  loadImage('img3.jpg'),
])
  .then(images => {
    console.log('Все изображения загружены');
    renderGallery(images);
  })
  .catch(() => {
    console.error('Не удалось загрузить одно из изображений');
  });

Promise.race() — первый завершённый промис

// Выполнить несколько операций, использовать первую успешную
Promise.race([
  fetch('api1.example.com/data'),
  fetch('api2.example.com/data'),
  fetch('api3.example.com/data'),
])
  .then(response => response.json())
  .then(data => console.log('Первый ответ:', data))
  .catch(error => console.error('Все API недоступны'));

// Практический пример: timeout
Promise.race([
  fetch('/api/users'),
  new Promise((_, reject) => 
    setTimeout(() => reject(new Error('Timeout')), 5000)
  ),
])
  .then(res => res.json())
  .catch(error => console.error('Ошибка или timeout:', error));

Async/Await — синтаксический сахар над Promise

// Промисы можно использовать с async/await (более современный подход)

// С промисами (старый способ)
function getUserData(id) {
  return fetch(`/api/users/${id}`)
    .then(res => res.json())
    .then(user => {
      return fetch(`/api/posts/${user.id}`)
        .then(res => res.json())
        .then(posts => ({ user, posts }));
    });
}

// С async/await (новый способ)
async function getUserData(id) {
  const userRes = await fetch(`/api/users/${id}`);
  const user = await userRes.json();
  
  const postsRes = await fetch(`/api/posts/${user.id}`);
  const posts = await postsRes.json();
  
  return { user, posts };
}

// Использование
getUserData(123)
  .then(data => console.log(data))
  .catch(error => console.error(error));

Практические примеры в React

Загрузка данных

import { useEffect, useState } from 'react';

function UserProfile({ id }: { id: string }) {
  const [user, setUser] = useState(null);
  const [loading, setLoading] = useState(true);
  const [error, setError] = useState(null);
  
  useEffect(() => {
    // Используем промис для загрузки данных
    fetch(`/api/users/${id}`)
      .then(res => res.json())
      .then(data => {
        setUser(data);
        setLoading(false);
      })
      .catch(err => {
        setError(err.message);
        setLoading(false);
      });
  }, [id]);
  
  if (loading) return <div>Загрузка...</div>;
  if (error) return <div>Ошибка: {error}</div>;
  
  return <div>{user.name}</div>;
}

Отправка формы

function ContactForm() {
  const handleSubmit = (e: React.FormEvent<HTMLFormElement>) => {
    e.preventDefault();
    
    const formData = new FormData(e.currentTarget);
    
    // Промис для отправки данных
    fetch('/api/contact', {
      method: 'POST',
      body: formData,
    })
      .then(res => {
        if (!res.ok) throw new Error('Ошибка сервера');
        return res.json();
      })
      .then(data => {
        alert('Спасибо за сообщение!');
      })
      .catch(error => {
        alert('Ошибка: ' + error.message);
      });
  };
  
  return (
    <form onSubmit={handleSubmit}>
      <input name="email" type="email" required />
      <button type="submit">Отправить</button>
    </form>
  );
}

Параллельная загрузка данных

function Dashboard() {
  const [data, setData] = useState(null);
  
  useEffect(() => {
    // Загрузить несколько источников данных одновременно
    Promise.all([
      fetch('/api/users').then(r => r.json()),
      fetch('/api/analytics').then(r => r.json()),
      fetch('/api/reports').then(r => r.json()),
    ])
      .then(([users, analytics, reports]) => {
        setData({ users, analytics, reports });
      })
      .catch(error => {
        console.error('Ошибка загрузки данных:', error);
      });
  }, []);
  
  if (!data) return <div>Загрузка...</div>;
  
  return (
    <div>
      <Users data={data.users} />
      <Analytics data={data.analytics} />
      <Reports data={data.reports} />
    </div>
  );
}

Проблемы и решения

Проблема: Callback Hell

// Плохо: вложенные callbacks
getUser(id, (err, user) => {
  if (!err) {
    getPosts(user.id, (err, posts) => {
      if (!err) {
        getComments(posts[0].id, (err, comments) => {
          if (!err) {
            console.log(comments);
          }
        });
      }
    });
  }
});

// Хорошо: промисы
getUser(id)
  .then(user => getPosts(user.id))
  .then(posts => getComments(posts[0].id))
  .then(comments => console.log(comments))
  .catch(error => console.error(error));

Проблема: утечка памяти

// Плохо: промис может оставить listener висеть
useEffect(() => {
  fetch('/api/data').then(res => res.json());
  // Если компонент демонтируется, промис выполнится всё равно!
}, []);

// Хорошо: отмена промиса через AbortController
useEffect(() => {
  const controller = new AbortController();
  
  fetch('/api/data', { signal: controller.signal })
    .then(res => res.json())
    .then(data => setData(data))
    .catch(error => {
      if (error.name !== 'AbortError') {
        console.error(error);
      }
    });
  
  return () => controller.abort(); // Отмена при демонтировании
}, []);

Заключение

Промисы нужны для:

  1. Асинхронных операций — сетевые запросы, работа с файлами, таймауты
  2. Обработки ошибок — единый способ обработать ошибки
  3. Цепочек операций — выполнить операции друг за другом
  4. Параллельных операций — Promise.all, Promise.race
  5. Управления потоком выполнения — контролировать порядок выполнения кода

Промисы — это фундамент современного JavaScript. Без понимания промисов невозможно писать современные приложения на React, Node.js и других технологиях.