Статический метод Promise.race() в JavaScript

В этом руководстве вы узнаете, как использовать статический метод JavaScript Promise.race().

Статический метод Promise.race() принимает список обещаний как итерируемый объект и возвращает новое обещание, которое выполняется или отклоняется, как только появляется одно обещание, которое выполняется или отклоняется, со значением или причиной этого обещания.

Синтаксис:

Promise.race(iterable)

В этом синтаксисе iterable — это итерируемый объект, содержащий список промисов.

Название Promise.race() подразумевает, что все промисы соревнуются друг с другом с одним победителем, либо разрешенным, либо отклоненным.

Следующая схема:

Promise.race()

На этой диаграмме:

  • promise1 выполняется со значением v1 в момент времени t1.
  • promise2 отклоняется с error в момент времени t2.
  • Поскольку promise1 разрешается раньше, чем promise2, promise1 выигрывает гонку. Поэтому Promise.race([promise1, promise2]) возвращает новое обещание, которое выполняется со значением v1 в момент времени t1.

Смотрите другую схему:

Диаграмма 2 - Promise.race()

На этой диаграмме:

  • promise1 выполняется с v1 в момент времени t2
  • promise2 отклонено с error в t1.
  • Поскольку promise2 разрешается раньше, чем promise1, promise2 выигрывает гонку. Поэтому Promise.race([promise1, promise2]) возвращает новое обещание, которое отклонено с error в момент времени t1.

Примеры

Давайте рассмотрим несколько примеров использования статического метода Promise.race().

1) Простые примеры

Следующее создает два обещания: одно разрешается за 1 секунду, а другое разрешается за 2 секунды. Поскольку первое обещание выполняется быстрее, чем второе, Promise.race() разрешается со значением из первого обещания:

const p1 = new Promise((resolve, reject) => {
    setTimeout(() => {
        console.log('The first promise has resolved');
        resolve(10);
    }, 1 * 1000);

});

const p2 = new Promise((resolve, reject) => {
    setTimeout(() => {
        console.log('The second promise has resolved');
        resolve(20);
    }, 2 * 1000);
});


Promise.race([p1, p2])
    .then(value => console.log(`Resolved: ${value}`))
    .catch(reason => console.log(`Rejected: ${reason}`));

Выход:

The first promise has resolved
Resolved: 10
The second promise has resolved

В следующем примере создаются два обещания. Первое обещание разрешается за 1 секунду, а второе отклоняется за 2 секунды. Поскольку первый промис быстрее второго, возвращаемый промис разрешается в значение из первого промиса:

const p1 = new Promise((resolve, reject) => {
    setTimeout(() => {
        console.log('The first promise has resolved');
        resolve(10);
    }, 1 * 1000);

});

const p2 = new Promise((resolve, reject) => {
    setTimeout(() => {
        console.log('The second promise has rejected');
        reject(20);
    }, 2 * 1000);
});


Promise.race([p1, p2])
    .then(value => console.log(`Resolved: ${value}`))
    .catch(reason => console.log(`Rejected: ${reason}`));

Выход:

The first promise has resolved 
Resolved: 10 
The second promise has rejected

Обратите внимание, что если бы второе обещание было быстрее, чем первое, обещание возврата было бы отклонено по причине второго обещания.

2) Практический пример

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

Для этого вы можете использовать статический метод Promise.race(). Если время ожидания истекло, вы показываете индикатор загрузки, в противном случае вы показываете сообщение.

Ниже показан код HTML:

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8">
    <title>JavaScript Promise.race() Demo</title>
    <link href="css/promise-race.css" rel="stylesheet">
</head>

<body>
    <div id="container">
        <button id="btnGet">Get Message</button>
        <div id="message"></div>
        <div id="loader"></div>
    </div>
    <script src="js/promise-race.js"></script>
</body>
</html>
Выход:

Для создания индикатора загрузки мы используем функцию анимации CSS. Дополнительную информацию см. в promise-race.css. С технической точки зрения, если элемент имеет класс .loader, он показывает индикатор загрузки.

  • Во-первых, определите новую функцию, которая загружает данные. Она использует setTimeout() для эмуляции асинхронной операции:
const DATA_LOAD_TIME = 5000;

function getData() {
    return new Promise((resolve, reject) => {
        setTimeout(() => {
            const message = 'Promise.race() Demo';
            resolve(message);
        }, DATA_LOAD_TIME);
    });
}
  • Во-вторых, разработайте функцию, которая показывает некоторое содержимое:
function showContent(message) {
    document.querySelector('#message').textContent = message;
}

Эту функцию также можно использовать для установки пустого message.

  • В-третьих, определите, что функция timeout() возвращает обещание, которое отклоняется при передаче TIMEOUT.
const TIMEOUT = 500;

function timeout() {
    return new Promise((resolve, reject) => {
        setTimeout(() => reject(), TIMEOUT);
    });
}
  • В-четвертых, разработайте пару функций, которые показывают и скрывают индикатор загрузки:
function showLoadingIndicator() {
    document.querySelector('#loader').className = 'loader';
}

function hideLoadingIndicator() {
    document.querySelector('#loader').className = '';
}
  • В-пятых, прикрепите прослушиватель событий щелчка к кнопке « Получить сообщение ». Внутри обработчика кликов используйте статический метод Promise.race() :
// handle button click event
const btn = document.querySelector('#btnGet');

btn.addEventListener('click', () => {
    // reset UI if users click the 2nd, 3rd, ... time
    reset();
    
    // show content or loading indicator
    Promise.race([getData()
            .then(showContent)
            .then(hideLoadingIndicator), timeout()
        ])
        .catch(showLoadingIndicator);
});

Мы передаем два промиса в метод Promise.race() :

Promise.race([getData()
            .then(showContent)
            .then(hideLoadingIndicator), timeout()
        ])
        .catch(showLoadingIndicator);

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

Если для выполнения первого промиса требуется более 500 мс, вызывается catch() для отображения индикатора загрузки. Как только первое обещание разрешается, оно скрывает индикатор загрузки.

  • Наконец, разработайте функцию reset(), которая скрывает сообщение и индикатор загрузки, если кнопка нажата во второй раз.
// reset UI
function reset() {
    hideLoadingIndicator();
    showContent('');
}

Объединим все это.

// after 0.5 seconds, if the getData() has not resolved, then show 
// the Loading indicator
const TIMEOUT = 500;
const DATA_LOAD_TIME = 5000;

function getData() {
    return new Promise((resolve, reject) => {
        setTimeout(() => {
            const message = 'Promise.race() Demo';
            resolve(message);
        }, DATA_LOAD_TIME);
    });
}

function showContent(message) {
    document.querySelector('#message').textContent = message;
}

function timeout() {
    return new Promise((resolve, reject) => {
        setTimeout(() => reject(), TIMEOUT);
    });
}

function showLoadingIndicator() {
    document.querySelector('#loader').className = 'loader';
}

function hideLoadingIndicator() {
    document.querySelector('#loader').className = '';
}


// handle button click event
const btn = document.querySelector('#btnGet');

btn.addEventListener('click', () => {
    // reset UI if users click the second time
    reset();

    // show content or loading indicator
    Promise.race([getData()
            .then(showContent)
            .then(hideLoadingIndicator), timeout()
        ])
        .catch(showLoadingIndicator);
});

// reset UI
function reset() {
    hideLoadingIndicator();
    showContent('');
}
Рейтинг
( Пока оценок нет )
Александр Русаков / автор статьи
Программист, разработчик, 12 лет опыта работы в крупных компаниях. Быстро освоил typescript, делюсь своими знаниями на страницах этого сайта.
Загрузка ...
JavaScript и TypeScript