🤔

await/catch を使うときの early return

2020/10/14に公開1

この記事は ポエム です

try/catchで書くとcatchの中で簡単にearly returnできますよね

(async () => {
  try {
    await new Promise((_, reject) => {
      reject('something went wrong!')
    })
  } catch(err) {
    console.log('caught!', err)
    return
  }
  console.log('ok~~')
})()

// caught! something went wrong!

こんな感じ

個人的にtry/catchの見た目が好きじゃないのでなるべく使いたくないってのもありますが、
Promiseにcatch生えてるのにtryする?って思うんですよね

でも単純に書き換えるだけだと

(async () => {
  const ret = await new Promise((_, reject) => {
    reject('something went wrong!')
  }).catch((err) => {
    console.log('caught!', err)
    return
  })
  console.log('ok~~')
})()

// caught! something went wrong!
// ok~~

「ok~~ ☆〜(ゝ。∂)」じゃあないんだよ

でも

(async () => {
  let success = true
  await new Promise((_, reject) => {
    reject('something went wrong!')
  }).catch((err) => {
    console.log('caught!', err)
    success = false
  })
  if(!success) {
    return
  }
  console.log('ok~~')
})()

// caught! something went wrong!

これじゃダサいですよね

だから最善手は、
戻り値のない処理でもなんとかresolve('_')とかやって、
Promise<void>としないように自分ルールを作った上で、

(async () => {
  const ret = await new Promise((_, reject) => {
    // これはresolveすることないですが…
    reject('something went wrong!')
  }).catch((err) => {
    console.log('caught!', err)
  })
  if(!ret) {
    return
  }
  console.log('ok~~')
})()

// caught! something went wrong!

とすることかなって思うんです

でも実態がPromise<void>なのにこれのためにstring返したりするの、
あんまりホメられたことじゃないと思うんですよね

みなさんどうしてるんですか?
やっぱりtry/catchですか?

GitHubで編集を提案

Discussion

みやびさんみやびさん

Promiseは非同期処理のコールバック地獄をメソッドチェーンで解決しているので、
JSの構文のtry/catchやfor文が機能しなくなりますね。
(これはPromiseの問題ではなく、非同期処理・イベントループの問題ですが)
なのでPromiseを魔改造して同期っぽく書ける糖衣構文(async/await)を用意して、半ば強引に解決させてます。

それを歪に感じる人が出るのは自然だと思います。
なのに中途半端に使ってしまうから気に入らない結果になったのでしょう。
シンプルにPromiseのthenで繋げば良さそうです。

const ret = new Promise((_, reject) => {
  reject('something went wrong!')
})

// わざわざret変数を定義したなら、メインのロジックと事後処理は分けた方が良さそう
ret.then(() => {
  console.log('ok~~')
}).catch(err => {
  console.log('caught!', err)
})

みなさんどうしてるんですか?
やっぱりtry/catchですか?

私はasync/awaitに違和感はあまり感じていないので、
async関数下のtry/catchで大満足です。

そもそもreject関数が早期リターンみたいなものですから、
Promiseの関数の中身を書き換えられるなら、これが良いでしょうね。

const ret = new Promise((_, reject) => {
  return reject('something went wrong!')
  console.log('ok~~')
}).catch(err => {
  console.log('caught!', err)
})