🔍

Работа с асинхронными вызовами функций

🗓️ 30 декабря 2020 г. 2 мин. чтения

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

Не используйте await везде, где вы используете промисы (Promises). Если вы загружаете более одного набора данных, старайтесь получать их параллельно.

Допустим, у нас есть какой-то запрос к API:

const request = value => new Promise(resolve => {
  setTimeout(() => resolve(value), 3000);
});

И есть три метода, которые позволяют нам получить какой-то набор данных:

const fetchFoo = () => request('foo');
const fetchBar = () => request('bar');
const fetchBaz = () => request('baz');

И наша задача — получить все данные и уже передать их дальше для дальнейшей обработки. Неоднократно я встречал в коде нечто подобное:

const getAllData = async () => {
  const foo = await fetchFoo();
  const bar = await fetchBar();
  const baz = await fetchBaz();

  return { foo, bar, baz };
};

В таком подходе есть проблема блокирования последующих запросов, не зависящих от результата выполнения предыдущих.

Каждый из вызовов функции request будет ожидать, пока не завершится предыдущий запрос. То есть функция fetchBar не вызовется до тех пор, пока fetchFoo не вернёт результат, а fetchBaz, в свою очередь, будет дожидаться fetchBar.

Один из вариантов решения этой проблемы, — это использовать Promise.all для параллельного получения данных:

const getAllData = async () => {
  const [
    foo,
    bar,
    baz
  ] = await Promise.all([
    fetchFoo(),
    fetchBar(),
    fetchBaz()
  ]);

  return { foo, bar, baz };
};

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

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

Есть второй способ, без использования Promise.all, но он более громоздкий:

const getAllData = async () => {
  const fooReady = fetchFoo();
  const barReady = fetchBar();
  const bazReady = fetchBaz();

  const foo = await fooReady;
  const bar = await barReady;
  const baz = await bazReady;

  return { foo, bar, baz };
};

Заключение

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

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

Copyright © 2021