Задача

Функционал массового редактирования позволяет выбрать несколько записей и редактировать все одновременно.

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

Откуда это замедление?

Код написан так:

for (r in records) {
await update(r);
}

При изменении одной записи делается API-вызов, на который уходит от 500 мс до 1 сек.

Поскольку записи перебираются и изменяются одна за другой, это время увеличивается линейно  —  по мере роста их числа.

Промис для ускорения

К счастью, не нужно ждать завершения изменения, прежде чем обработать следующее. Promise.allSettled() позволяет делать запрос его изменения до завершения предыдущего.

Вот что мы сделали:

const allPromises = [];

for (r in records) {
  const promise = update(r);
  allPromises.push(promise);
};

await Promise.allSettled(allPromises);

Проиллюстрируем то, что происходит при использовании async/await и promises:

async/await и promise

С promise не нужно ждать возвращения каждого запроса на изменение, прежде чем делать следующий. Так экономится много времени.

Получение результатов промисов

К сожалению, иногда update(r) завершается неуспешно.

Если какие-то изменения неуспешны, выявить их можно по разрешенному значению в Promise.allSettled(). Здесь выдается массив, каждым объектом которого описывается результат промиса.

Если промис выполнен, получаем {status: “fulfilled”, value: xxx }.

Если отклонен  —  {status: “rejected”, reason: xxx }.

Например:

const values = await Promise.allSettled([
  Promise.resolve(33),
  Promise.reject(new Error('an error'))
])
console.log(values)

// [
//   {status: "fulfilled", value: 33},
//   {status: "rejected",  reason: Error: an error}
// ]

В этом случае нужно узнать id записей с успешными и неуспешными изменениями.

Вот что мы сделали:

const allPromises = [];
for (r in records) {
    const promise = new Promise((res, rej) => {
        update(t)
            .then(() => { res(r.id) }); # успешно
            .catch(() => { rej(r.id) }); # неуспешно
    });
    allPromises.push(promise);
};

const outcomes = await Promise.allSettled(allPromises);

const succeeded = outcomes.filter(o => o.status === "fulfilled");
const succeededIds = succeeded.map(s => s.value);

const failed = outcomes.filter(o => o.status === "rejected");
const failedIds = failed.map(f => f.reason);

Проиллюстрируем то, что происходит, когда промисы неуспешны:

В возвращаемом массиве указано, какие успешны, а какие нет.

Вот и всё. Благодаря смене Async/Await на Promises.allSettled() удалось ускорить операцию массового редактирования более чем 50 записей с более чем 50 сек до менее 5 сек.

Читайте также:

Читайте нас в Telegram, VK и Дзен


Перевод статьи Wendie Sun: Changing Async/Await to Promises.allSettled() to Speed Up API Calls in Node.JS

Предыдущая статьяКак создать инструмент PGP-шифрования на основе Python
Следующая статьяСоздание расширяющих методов на C#