【JavaScript】任意の既存の Promise オブジェクトにタイムアウト機能をつける

 Promise は JavaScript の非同期処理の要です。JavaScript内の非同期処理はそのほとんどがPromise オブジェクトとして表され、それを処理することで非同期処理を実現します。
 Promise – JavaScript | MDN
 終了タイミングが不定な非同期処理は完了をいつまで待つか決めておくタイムアウトの仕組みを備えておく方が良い時が多いです。いつまで待つかを決めずに終わりが見えないほど長大な処理の完了の知らせを待ち続けると PC のリソースを食べ続けますし、ユーザーもいつまでも待ちがちです。タイムアウト付きの非同期処理は例えば次の様に実装できます。

const makeMainPromise = () => {
    return new Promise((resolve, reject) => {
        // Promise 中の処理が始まった時点であらかじめ時限式の処理失敗を仕込む
        setTimeout(() => reject('timeout'), 9000);
        
        /*
         * 処理本体
         */
        // ↑のタイムアウトによる reject が走るまでに resolve すると正常終了する
        resolve('返り値');
    });
}

makeMainPromise()
  .then(res => console.log(res))// 返り値
  .catch(err => console.log(err)); // timeout

 完全に自作の非同期処理ならば上記の様にタイムアウトをあらかじめ仕込んでおけますが、タイムアウト機能のない処理の Promise オブジェクトにタイムアウトを付けたくなる場合があります。これは外部ライブラリ内の処理で生まれた Promise にタイムアウト機能を付けたいとかそういった場合です。これは次の様に Promise.race を用いることで実装できます。

// 外部から渡される Promise 例。この promiseExample  は Promise オブジェクトの例であって実装はどうでもいいです。
// OK ボタンをクリックしたら resolve
// NG ボタンをクリックしたら reject
const promiseExample = new Promise((resolve, reject) => {
  const okEl = document.createElement('button');
  okEl.innerText = 'OK';
  okEl.addEventListener('click', () => {
    resolve('OK');
  });

  const ngEl = document.createElement('button');
  ngEl.innerText = 'NG';
  ngEl.addEventListener('click', () => {
    reject('NG');
  });

  const boxEl = document.createElement('div');
  boxEl.className = 'ok_or_ng_btn';
  boxEl.style.position = 'fixed';
  boxEl.style.top = '50%';
  boxEl.style.left = '50%';
  boxEl.style.background = '#fff';
  boxEl.appendChild(okEl);
  boxEl.appendChild(ngEl);
  document.body.appendChild(boxEl);
});

// タイムアウトが起きたことを示すための Promise を生成する関数
const makeTimeoutPromise = (timeMs) =>
  new Promise(function(resolve, reject) {
    // makeTimeoutPromise の引数に渡されたミリ秒が経つと reject を投げる
    setTimeout(() => reject(`timeout`), timeMs);
  });

// Promise.race を用います。
// @see https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Global_Objects/Promise/race
// これにより外から渡された Promise が完了すればそれを、
// 解決前に tiemoutProimse が reject を投げればそれを処理します。
Promise.race([promiseExample, makeTimeoutPromise(10000)])
  .then((r) => console.log(r))
  .catch((e) => console.error(e));

 Promise.race を用いて、元からある外の Promise が先に完了すればそれを、タイムアウト用の Promise が先に完了すればそれを結果として採用します。
Promise.race() – JavaScript | MDN

>株式会社シーポイントラボ

株式会社シーポイントラボ

TEL:053-543-9889
営業時間:9:00~18:00(月〜金)
住所:〒432-8003
   静岡県浜松市中央区和地山3-1-7
   浜松イノベーションキューブ 315
※ご来社の際はインターホンで「316」をお呼びください

CTR IMG