LoginSignup
5
9

More than 5 years have passed since last update.

JavaScript でリトライ付きの Promise を作る

Last updated at Posted at 2018-11-06

例: サーバが 502 を返したときに sleep(setTimeout) を入れつつ 10 回までリトライしたい。

コピペ用

最後のエラーだけ返す版

reject 時に最後のエラーが返ってきます

function retry<T>(asyncFunc: ()=> Promise<T>, retry = 3): Promise<T> {
    const uniqueObj = {};
    const nums = Array.from(Array(retry));
    return nums.reduce((prm, _, i)=>
        prm.catch((err)=>
            err !== uniqueObj
                ? Promise.reject(err)
                : asyncFunc()
                    .catch((err)=>
                        sleep(i*1000)
                            .then(()=> Promise.reject(uniqueObj)) )
        )
    , Promise.reject(uniqueObj));
    function sleep(ms: number): Promise<void> {
        return new Promise((r)=> setTimeout(r, ms))
    }
}

全てのエラーを配列で返す版

reject 時に過去の失敗すべてのエラーが配列で返ってきます

function retry<T>(asyncFunc: ()=> Promise<T>, retry = 3): Promise<T> {
    const errs = [];
    const nums = Array.from(Array(retry));
    return nums.reduce((prm, _, i)=>
        prm.catch((err)=>
            err !== errs
                ? Promise.reject(err)
                : asyncFunc()
                    .catch((err)=>{
                        errs.push(err);
                        return sleep(i*1000)
                            .then(()=> Promise.reject(errs))
                    })
        )
    , Promise.reject(errs));
    function sleep(ms: number): Promise<void> {
        return new Promise((r)=> setTimeout(r, ms))
    }
}

usage

リトライしたい非同期関数をクロージャで囲むだけで使える

const body = await retry(()=> fetch("https://example.com").then((res)=> res.text()));

あるいは async 関数を渡す

const body = await retry(async ()=>{
   const res = await fetch("https://example.com")
   const body = await res.text();
   return body;
});
5
9
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
5
9