JavaScript
Node.js

return new Promise()とreturn Promise.resolve()使い分け

伝えたいこと

javascriptを触っていて嫌でも触らないといけないPromiseオブジェクトの返し方でreturn new Promise()return Promise.resolve()の使い分け方法。
結論から言うと再帰関数を使用する際に明確な違いが生まれる。
以下コードと少し解説です。

return Promise.resolve()

const correct = 1

const generateRand = (digit) => {
  return Math.floor(Math.random() * digit)
}

const getCorrect = () => {
  return new Promise(resolve => {
    const delayTime = generateRand(1000)
    console.log(`delayTime: ${delayTime}ms`)
    setTimeout(() => {
      return resolve(correct)
    }, delayTime)
  })
}

const findCorrect = (findVal, counter) => {
  return getCorrect().then(correct => {
    if (findVal !== correct) {
      return findCorrect(generateRand(10), counter + 1)
    }
    return Promise.resolve({correct: findVal, counter: counter})
  })
}

findCorrect(generateRand(10), 0).then(correctItem => {
  console.log(correctItem.correct)
  console.log(`${correctItem.counter + 1}回処理されました。`)
})
result
delayTime: 66ms
delayTime: 176ms
delayTime: 998ms
delayTime: 283ms
delayTime: 816ms
delayTime: 735ms
delayTime: 397ms
delayTime: 501ms
delayTime: 908ms
delayTime: 549ms
delayTime: 12ms
delayTime: 833ms
delayTime: 698ms
delayTime: 378ms
delayTime: 616ms
delayTime: 935ms
1
16回処理されました。

コードの説明をすると正解の数字1があり、ランダム生成した値と一致するまで再帰処理を行うというプログラムです。(catch、rejectはコード省略化のため書いていません)

getCorrect関数が正解の数字を返してくれます。が、非同期処理です。
はいはいPromisePromise。
getCorrect関数がPromiseオブジェクトを返すということに伴ってfindCorrect関数も必然的にPromiseを返さないといけません。
処理的にはfindCorrect関数が正解の数字と一致するまで再帰呼び出しを行うのみですね。
無事resultでも正解の数字と何回再帰処理が行われたのかが出力されていますね。

ところが・・・

return new Promise()

const correct = 1

const generateRand = (digit) => {
  return Math.floor(Math.random() * digit)
}

const getCorrect = () => {
  return new Promise(resolve => {
    const delayTime = generateRand(1000)
    console.log(`delayTime: ${delayTime}ms`)
    setTimeout(() => {
      return resolve(correct)
    }, delayTime)
  })
}

const findCorrect = (findVal, counter) => {
  return new Promise(resolve => {
    return getCorrect().then(correct => {
      if (findVal !== correct) {
        return findCorrect(generateRand(10), counter + 1)
      }
      return resolve({correct: findVal, counter: counter})
    })
  })
}

findCorrect(generateRand(10), 0).then(correctItem => {
  console.log(correctItem.correct)
  console.log(`${correctItem.counter + 1}回処理されました。`)
})
result
delayTime: 148ms
delayTime: 788ms
delayTime: 563ms
delayTime: 908ms
delayTime: 962ms
delayTime: 429ms
delayTime: 934ms
delayTime: 306ms
delayTime: 178ms
delayTime: 555ms
delayTime: 978ms
delayTime: 572ms
delayTime: 822ms
delayTime: 429ms
delayTime: 16ms
delayTime: 291ms
delayTime: 20ms
delayTime: 272ms

return Promise.resolve()との違いはfindCorrect関数の戻り値がreturn new Promise()になっていることですね。
return new Promise()の場合、遅延時間のみが出力されてresolve()の値が返る前に処理が終了しています。
再帰呼び出しの際に毎回新しいPromiseオブジェクトを作成しているから当たり前といえば当たり前なのですが、今まで意識せずにreturn new Promise()で書いていたため、ハマってしまいました。

教訓

Promiseオブジェクトを用いた処理で再起呼び出しを行う場合はPromise.resolve()Promise.reject()を使うこと。