Понимание Promise.all() в деталях

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

Метод Promise.all() является одним из методов параллелизма промисов. Это может быть полезно для агрегирования результатов нескольких промисов. Обычно он используется, когда есть несколько связанных асинхронных задач, от которых зависит успешная работа всего кода — все, что мы хотим выполнить до продолжения выполнения кода.

Статический метод Promise.all() принимает в качестве входных данных итерацию промисов и возвращает один промис.

Статический метод Promise.all() принимает в качестве входных данных итерацию промисов и возвращает один промис. Это возвращенное обещание выполняется, когда выполняются все обещания ввода (включая передачу пустой итерации) с массивом значений выполнения. Он отклоняется, когда любое из входных обещаний отклоняется с этой первой причиной отклонения.

Синтаксис

Promise.all(iterable)

Параметры

итерируемый —итерируемый (например, массив) обещаний.

Возвращаемое значение

Обещание, которое:

  • Уже выполнено, если переданная итерация пуста
  • Выполняется асинхронно, когда выполняются все промисы в данном итерируемом объекте. Значение выполнения представляет собой массив значений выполнения в порядке переданных промисов, независимо от порядка выполнения. Если переданный итерируемый объект не пуст, но не содержит ожидающих обещаний, возвращенное обещание по-прежнему выполняется асинхронно (а не синхронно).
  • Асинхронно отклоняется, когда любое из обещаний в данной итерации отклоняется. Причина отклонения — это причина отклонения первого обещания, которое было отклонено.

Описание

Метод Promise.all() является одним из методов параллелизма промисов. Это может быть полезно для агрегирования результатов нескольких промисов. Обычно он используется, когда есть несколько связанных асинхронных задач, от которых зависит успешная работа всего кода — все, что мы хотим выполнить до продолжения выполнения кода.

Promise.all() отклонит сразу после отклонения любого входного промиса. Для сравнения, обещание, возвращаемое Promise.allSettled(), будет ожидать завершения всех введенных обещаний, независимо от того, отклонено одно из них или нет. Используйте allSettled(), если вам нужен окончательный результат каждого обещания во входной итерации.

Примеры

Promise.all ждет всех исполнений (или первого отклонения).

Использование Promise.all()

const p1 = Promise.resolve(3);
const p2 = 1337;
const p3 = new Promise((resolve, reject) => {
  setTimeout(() => {
    resolve("foo");
  }, 100);
});

Promise.all([p1, p2, p3]).then((values) => {
  console.log(values); // [3, 1337, "foo"]
}

Если итерируемый объект содержит значения, не являющиеся обещаниями, они будут проигнорированы, но все равно учтены в возвращаемом значении массива обещаний (если обещание выполнено:

// All values are non-promises, so the returned promise gets fulfilled
const p = Promise.all([1, 2, 3]);
// The only input promise is already fulfilled,
// so the returned promise gets fulfilled
const p2 = Promise.all([1, 2, 3, Promise.resolve(444)]);
// One (and the only) input promise is rejected,
// so the returned promise gets rejected
const p3 = Promise.all([1, 2, 3, Promise.reject(555)]);

// Using setTimeout, we can execute code after the queue is empty
setTimeout(() => {
  console.log(p);
  console.log(p2);
  console.log(p3);
});

// Logs:
// Promise { <state>: "fulfilled", <value>: Array[3] }
// Promise { <state>: "fulfilled", <value>: Array[4] }
// Promise { <state>: "rejected", <reason>: 555 }

Асинхронность или синхронность Обещания

В следующем примере демонстрируется асинхронность Promise.all при передаче непустого итерируемого объекта:

// Passing an array of promises that are already resolved,
// to trigger Promise.all as soon as possible
const resolvedPromisesArray = [Promise.resolve(33), Promise.resolve(44)];

const p = Promise.all(resolvedPromisesArray);
// Immediately logging the value of p
console.log(p);

// Using setTimeout, we can execute code after the queue is empty
setTimeout(() => {
  console.log("the queue is now empty");
  console.log(p);
});

// Logs, in order:
// Promise { <state>: "pending" }
// the queue is now empty
// Promise { <state>: "fulfilled", <value>: Array[2] }

То же самое происходит, если Promise.all отклоняет:

const mixedPromisesArray = [Promise.resolve(33), Promise.reject(44)];
const p = Promise.all(mixedPromisesArray);
console.log(p);
setTimeout(() => {
  console.log("the queue is now empty");
  console.log(p);
});

// Logs:
// Promise { <state>: "pending" }
// the queue is now empty
// Promise { <state>: "rejected", <reason>: 44 }

Promise.all разрешается синхронно тогда и только тогда, когда переданная итерация пуста:

const p = Promise.all([]); // Will be immediately resolved
const p2 = Promise.all([1337, "hi"]); // Non-promise values are ignored, but the evaluation is done asynchronously
console.log(p);
console.log(p2);
setTimeout(() => {
  console.log("the queue is now empty");
  console.log(p2);
});

// Logs:
// Promise { <state>: "fulfilled", <value>: Array[0] }
// Promise { <state>: "pending" }
// the queue is now empty
// Promise { <state>: "fulfilled", <value>: Array[2] }

Использование Promise.all() с асинхронными функциями

В асинхронных функциях очень часто приходится «переждать» ваш код. Например, при наличии следующих функций:

function promptForDishChoice() {
  return new Promise((resolve, reject) => {
    const dialog = document.createElement("dialog");
    dialog.innerHTML = `
<form method="dialog">
  <p>What would you like to eat?</p>
  <select>
    <option value="pizza">Pizza</option>
    <option value="pasta">Pasta</option>
    <option value="salad">Salad</option>
  </select>
  <menu>
    <li><button value="cancel">Cancel</button></li>
    <li><button type="submit" value="ok">OK</button></li>
  </menu>
</form>
    `;
    dialog.addEventListener("close", () => {
      if (dialog.returnValue === "ok") {
        resolve(dialog.querySelector("select").value);
      } else {
        reject(new Error("User cancelled dialog"));
      }
    });
    document.body.appendChild(dialog);
    dialog.showModal();
  });
}

async function fetchPrices() {
  const response = await fetch("/prices");
  return await response.json();
}

Вы можете написать такую ​​функцию:

async function getPrice() {
  const choice = await promptForDishChoice();
  const prices = await fetchPrices();
  return prices[choice];
}

Однако обратите внимание, что выполнение promptForChoice и fetchPrices не зависит от результата друг друга. Пока пользователь выбирает свое блюдо, можно получать цены в фоновом режиме, но в приведенном выше коде оператор ожидания заставляет асинхронную функцию приостанавливаться до тех пор, пока не будет сделан выбор, а затем снова до тех пор, пока цены не будут получены. Мы можем использовать Promise.all для их одновременного запуска, чтобы пользователю не приходилось ждать получения цен до получения результата:

async function getPrice() {
  const [choice, prices] = await Promise.all([
    promptForDishChoice(),
    fetchPrices(),
  ]);
  return prices[choice];
}

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

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

async function getPrice() {
  const [choice, prices] = await Promise.all([
    promptForDishChoice,
    fetchPrices,
  ]);
  // `choice` and `prices` are still the original async functions;
  // Promise.all() does nothing to non-promises
}

Promise.all отказоустойчивое поведение

Promise.all отклоняется, если какой-либо из элементов отклонен. Например, если вы передаете четыре обещания, которые разрешаются после тайм-аута, и одно обещание, которое немедленно отклоняется, то Promise.all будет отклонен немедленно.

const p1 = new Promise((resolve, reject) => {
  setTimeout(() => resolve("one"), 1000);
});
const p2 = new Promise((resolve, reject) => {
  setTimeout(() => resolve("two"), 2000);
});
const p3 = new Promise((resolve, reject) => {
  setTimeout(() => resolve("three"), 3000);
});
const p4 = new Promise((resolve, reject) => {
  setTimeout(() => resolve("four"), 4000);
});
const p5 = new Promise((resolve, reject) => {
  reject(new Error("reject"));
});

// Using .catch:
Promise.all([p1, p2, p3, p4, p5])
  .then((values) => {
    console.log(values);
  })
  .catch((error) => {
    console.error(error.message);
  });

// Logs:
// "reject"

Это поведение можно изменить, обработав возможные отклонения:

const p1 = new Promise((resolve, reject) => {
  setTimeout(() => resolve("p1_delayed_resolution"), 1000);
});

const p2 = new Promise((resolve, reject) => {
  reject(new Error("p2_immediate_rejection"));
});

Promise.all([p1.catch((error) => error), p2.catch((error) => error)]).then(
  (values) => {
    console.log(values[0]); // "p1_delayed_resolution"
    console.error(values[1]); // "Error: p2_immediate_rejection"
  }
);

Кроме того, чтобы получать уведомления о моих новых статьях и историях: Подпишитесь на меня на Medium.

Подпишитесь на мой Канал YouTube, чтобы получать образовательные материалы по схожим темам

Следуйте за мной на Medium и GitHub, чтобы быстро подключаться

Вы можете найти меня в LinkedIn, так как это профессиональная сеть для таких людей, как я и вы.

Ваше здоровье !!!!!