JavaScript Promise.race()

サマリー: このチュートリアルでは、JavaScript Promise.race() static メソッドの使い方について学びます。

JavaScript Promise.race() static メソッドの概要

Promise.race() static メソッドは、イテラブル オブジェクトとして プロミス のリストを受け取り、プロミスが1つでも解決するか拒否されるとすぐに解決または拒否される新しいプロミスを、そのプロミスからの値または理由で返します。

Promise.race() メソッドの構文はこちらです

Promise.race(iterable)Code language: JavaScript (javascript)

この構文で、iterable はプロミスのリストを含む イテラブル オブジェクト です。

Promise.race() の名前は、すべてのプロミスが 1 つの勝者(解決または拒否)となって競争していることを示しています。

次の図を参照してください

この図で

  • promise1t1 で値 v1 で解決されます。
  • promise2t2error で拒否されます。
  • promise1promise2 より早く解決されるため、promise1 がレースに勝ちます。そのため、Promise.race([promise1, promise2])t1 で値 v1 で解決される新しいプロミスを返します。

別の図を参照してください

この図で

  • promise1t2v1 で解決されます。
  • promise2t1error で拒否されます。
  • promise2promise1 より早く解決されるため、promise2 がレースに勝ちます。そのため、Promise.race([promise1, promise2])t1error で拒否される新しいプロミスを返します。

JavaScript Promise.race() の例

Promise.race() static メソッドを使用する例をいくつか見てみましょう。

1) シンプルな JavaScript Promise.race() の例

次のコードは 2 つのプロミスを作成します。1 つは 1 秒後に解決し、もう 1 つは 2 秒後に解決します。最初のプロミスは 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}`));
Code language: JavaScript (javascript)

出力

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

次の例は、2 つのプロミスを作成します。最初のプロミスは 1 秒後に解決しますが、2 番目のプロミスは 2 秒後に拒否されます。最初のプロミスは 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}`));
Code language: JavaScript (javascript)

出力

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

2 番目のプロミスの方が 1 番目のプロミスよりも早ければ、返されるプロミスは 2 番目のプロミスの理由で拒否されます。

2) 実践的な JavaScript Promise.race() の例

サーバーからのデータロード処理が一定秒数を超えた場合にスピナーを表示する必要があるとします。

これを実現するには、Promise.race() static メソッドを使用できます。タイムアウトが発生した場合、ローディングインジケータを表示し、そうでない場合、メッセージを表示します。

以下は 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>
Code language: HTML, XML (xml)

ロードインジケーターを作成するために 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);
    });
}
Code language: JavaScript (javascript)

次に、コンテンツを表示する関数を開発します。

function showContent(message) {
    document.querySelector('#message').textContent = message;
}Code language: JavaScript (javascript)

また、この関数は message を空白に設定するためにも使用できます。

3つ目に、プロミスを返す timeout() 関数を定義します。指定された TIMEOUT が渡されると、プロミスは拒否されます。

const TIMEOUT = 500;

function timeout() {
    return new Promise((resolve, reject) => {
        setTimeout(() => reject(), TIMEOUT);
    });
}Code language: JavaScript (javascript)

4つ目に、ロードインジケーターを表示して非表示にする関数を開発します。

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

function hideLoadingIndicator() {
    document.querySelector('#loader').className = '';
}Code language: JavaScript (javascript)

5つ目に、メッセージを取得ボタンにクリックイベントリスナーを追加します。クリックハンドラー内で、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);
});Code language: JavaScript (javascript)

Promise.race() メソッドに2つのプロミスを渡します。

Promise.race([getData()
            .then(showContent)
            .then(hideLoadingIndicator), timeout()
        ])
        .catch(showLoadingIndicator);Code language: JavaScript (javascript)

最初のプロミスは、サーバーからデータを取得し、コンテンツを表示し、ロードインジケーターを非表示にします。2つ目のプロミスは、タイムアウトを設定します。

最初のプロミスが解決するのに 500 ms 以上かかると、ロードインジケーターを表示するために catch() が呼び出されます。最初のプロミスが解決されると、ロードインジケーターが非表示になります。

最後に、ボタンを 2 回目クリックした場合にメッセージとロードインジケーターを非表示にする reset() 関数を開発します。

// reset UI
function reset() {
    hideLoadingIndicator();
    showContent('');
}Code language: JavaScript (javascript)

すべてまとめて配置してください。

// 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('');
}Code language: JavaScript (javascript)

まとめ

  • Promise.race(iterable) メソッドは、イテラブル内のプロミスのいずれかが解決または拒否されるとすぐに、その値またはエラーを使用して、解決または拒否する新しいプロミスを返します。

クイズ

これは役に立つチュートリアルでしたか ?