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

Как получаются значения асинхронной операции?

1.0 Junior🔥 111 комментариев
#Soft Skills и рабочие процессы

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

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

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

Получение значений асинхронных операций

Что такое асинхронная операция

Асинхронная операция — это операция, которая не выполняется сразу и возвращает результат не сразу, а в будущем. Примеры:

- Запрос к API
- Чтение файла
- Таймер
- Событие клика

Способы получить значения

1. Callbacks (старый способ, устаревший)

// Плохо: callback hell
fetchUser(1, (user) => {
  fetchPosts(user.id, (posts) => {
    fetchComments(posts[0].id, (comments) => {
      console.log(comments);
    });
  });
});

Проблемы:

  • Читается справа налево (pyramid of doom)
  • Сложно обрабатывать ошибки
  • Сложно отменить операцию

2. Promises (современный стандарт)

// Хорошо: читается слева направо
fetchUser(1)
  .then((user) => fetchPosts(user.id))
  .then((posts) => fetchComments(posts[0].id))
  .then((comments) => console.log(comments))
  .catch((error) => console.error(error));

Структура Promise:

const promise = new Promise((resolve, reject) => {
  // Асинхронная операция
  setTimeout(() => {
    if (success) {
      resolve('Успешный результат'); // Выполнено
    } else {
      reject('Ошибка'); // Отклонено
    }
  }, 1000);
});

promise
  .then((result) => console.log(result))
  .catch((error) => console.error(error))
  .finally(() => console.log('Готово'));

3. Async/Await (самый современный способ)

// Лучше всего: выглядит как синхронный код
async function getComments() {
  try {
    const user = await fetchUser(1);
    const posts = await fetchPosts(user.id);
    const comments = await fetchComments(posts[0].id);
    console.log(comments);
    return comments;
  } catch (error) {
    console.error(error);
  }
}

await getComments();

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

1. Fetch данных с API

function Posts() {
  const [posts, setPosts] = useState<Post[]>([]);
  const [loading, setLoading] = useState(true);
  const [error, setError] = useState<string | null>(null);

  useEffect(() => {
    const fetchData = async () => {
      try {
        setLoading(true);
        const response = await fetch('/api/posts');
        if (!response.ok) throw new Error('Failed');
        const data = await response.json();
        setPosts(data);
      } catch (err) {
        setError(err instanceof Error ? err.message : 'Unknown error');
      } finally {
        setLoading(false);
      }
    };

    fetchData();
  }, []);

  if (loading) return <div>Loading...</div>;
  if (error) return <div>Error: {error}</div>;

  return (
    <ul>
      {posts.map((post) => (
        <li key={post.id}>{post.title}</li>
      ))}
    </ul>
  );
}

2. Promise.all для параллельных операций

// Плохо: последовательное выполнение (долго)
async function getDataSequential() {
  const user = await fetchUser(1);      // 1 сек
  const posts = await fetchPosts(1);    // 1 сек
  const comments = await fetchComments(); // 1 сек
  return { user, posts, comments };     // Всего: 3 сек
}

// Хорошо: параллельное выполнение (быстро)
async function getDataParallel() {
  const [user, posts, comments] = await Promise.all([
    fetchUser(1),       // 1 сек
    fetchPosts(1),      // 1 сек (параллельно)
    fetchComments(),    // 1 сек (параллельно)
  ]);                   // Всего: 1 сек
  return { user, posts, comments };
}

3. Promise.race для таймаутов

// Либо данные пришли, либо истёк timeout
const data = await Promise.race([
  fetch('/api/data'),
  new Promise((_, reject) =>
    setTimeout(() => reject(new Error('Timeout')), 5000)
  ),
]);

TanStack Query для асинхронных данных (рекомендуется)

import { useQuery } from '@tanstack/react-query';

function Posts() {
  // Все сложности с кэшированием, retry, loading states
  // решаются автоматически
  const { data: posts, isLoading, error } = useQuery({
    queryKey: ['posts'],
    queryFn: async () => {
      const res = await fetch('/api/posts');
      return res.json();
    },
  });

  if (isLoading) return <div>Loading...</div>;
  if (error) return <div>Error</div>;

  return <PostsList posts={posts} />;
}

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

1. Try/Catch

async function fetchData() {
  try {
    const response = await fetch('/api/data');
    const data = await response.json();
    return data;
  } catch (error) {
    // Ловим сетевые ошибки и ошибки парсинга JSON
    console.error('Failed to fetch:', error);
    throw error; // Пробросим дальше если нужно
  }
}

2. Проверка статуса

async function fetchData() {
  const response = await fetch('/api/data');
  
  // fetch НЕ бросает ошибку на 404/500!
  if (!response.ok) {
    throw new Error(`HTTP error! status: ${response.status}`);
  }
  
  return response.json();
}

3. Custom Error Handling

class APIError extends Error {
  constructor(
    public status: number,
    public data: any,
    message?: string
  ) {
    super(message || `API Error ${status}`);
  }
}

async function fetchWithErrorHandling(url: string) {
  const response = await fetch(url);
  
  if (!response.ok) {
    const data = await response.json();
    throw new APIError(response.status, data);
  }
  
  return response.json();
}

try {
  const data = await fetchWithErrorHandling('/api/data');
} catch (error) {
  if (error instanceof APIError) {
    console.log(`API returned ${error.status}: ${error.data.message}`);
  }
}

Отмена асинхронных операций

function Posts() {
  const [posts, setPosts] = useState([]);

  useEffect(() => {
    const controller = new AbortController();

    const fetchData = async () => {
      try {
        const response = await fetch('/api/posts', {
          signal: controller.signal, // Передаём сигнал
        });
        const data = await response.json();
        setPosts(data);
      } catch (error) {
        if (error instanceof DOMException && error.name === 'AbortError') {
          console.log('Fetch was cancelled');
        }
      }
    };

    fetchData();

    // Отмены fetch если компонент умрёт
    return () => controller.abort();
  }, []);

  return <div>{posts.length} posts</div>;
}

Последовательность операций с async/await

// Операция 2 зависит от операции 1
async function createAndFetchPost() {
  try {
    // 1. Создаём пост
    const createResponse = await fetch('/api/posts', {
      method: 'POST',
      body: JSON.stringify({ title: 'New Post' }),
    });
    const newPost = await createResponse.json();

    // 2. Затем загружаем все посты
    const allResponse = await fetch('/api/posts');
    const allPosts = await allResponse.json();

    return { newPost, allPosts };
  } catch (error) {
    console.error('Failed:', error);
  }
}

Вывод

Современный фронтенд в 2026:

1. Используй async/await, а не .then().then()
2. Для асинхронных данных в React — используй TanStack Query
3. Всегда обрабатывай ошибки (try/catch или .catch())
4. Для параллельных операций — Promise.all()
5. Для таймаутов — Promise.race() или AbortController
6. Не забывай отменять запросы при unmount компонента

Асинхронность — основа современного веб-разработки, важно понимать, как работают Promises и async/await.

Как получаются значения асинхронной операции? | PrepBro