コハム

Webクリエイターのコハムが提供する、Web制作に役立つ情報をまとめたブログです。

Promise.tryによる同期/非同期処理のエラーハンドリングのベストプラクティス

Promise.try to improve error handling and sync/async interoperability

記事は上記記事を意訳したものです。

※当ブログでの翻訳記事は元サイト様に許可を得て掲載しています。


この記事では、JavaScriptのPromiseと同じくらい古い面倒なタスクの現代的な処理方法について説明しています。プログラムを簡素化する機能を仕様に追加するトレンドに続き、今日は進行中のECMAScriptプロポーザルの段階を順調に進んでいるPromise.tryについて説明します。

関数の1つがPromiseを返し、もう1つが同期的に値を返すと仮定すると、次のように組み合わせることができます。

const retSync = () => "sync return";
const retAsync = () => new Promise((r) => r("async return"));

(async () =>
  retAsync()
    .then(console.log)
    .then(retSync)
    .then(console.log)
    .catch(console.error))();

// async return
// sync return

順序を入れ替えて、同期関数を先に置いてみましょう。

// 🚨 this is not going to work

const retSync = () => "sync return";
const retAsync = () => new Promise((r) => r("async return"));

(async () =>
  retSync()
    .then(console.log)
    .then(retAsync)
    .then(console.log)
    .catch(console.error))();

// TypeError: retSync().then is not a function. (In 'retSync().then(console.log)', 'retSync().then' is undefined)

retSyncの返り値は promiseではないため、そのようにはできません。同期的に返す関数をpromiseでラップする必要があります。

const retSync = () => "sync return";
const retAsync = () => new Promise((r) => r("async return"));

(async () =>
  Promise.resolve()
    .then(retSync)
    .then(console.log)
    .then(retAsync)
    .then(console.log)
    .catch(console.error))();

// sync return
// async return

これで動作しますが、イベントループサイクルを無駄にしています。しかし、解決方法があります!

const retSync = () => "sync return";
const retAsync = () => new Promise((r) => r("async return"));

(async () =>
  new Promise((resolve) => resolve(retSync()))
    .then(console.log)
    .then(retAsync)
    .then(console.log)
    .catch(console.error))();

// sync return
// async return

ようやく動作しましたが、面倒ではありませんか?同期か非同期かを気にすることなく、組み合わせるとよいでしょう。また、同期と非同期の実行のエラー処理を個別に書かずに、一箇所で処理できる共通のcatch句が便利です。

すでにエコシステムには多くのソリューションがあります。Sindre Sorhusのp-try、Bluebird のPromise.try / Promise.attemptes6-promise-tryなどがあります。また、ECMAScript提案の段階を進んでいるネイティブのPromise.tryもあり、間もなく言語仕様の一部になると確信しています。

const retSync = () => "sync return";
const retAsync = () => new Promise((r) => r("async return"));

(async () =>
  Promise.try(retSync)
    .then(console.log)
    .then(retAsync)
    .then(console.log)
    .catch(console.error))();

// sync return
// async return

この記事が、Promise.tryの目的と解決しようとしている問題の種類を理解するのに役立ったことを願っています。コーディングを続けていきましょう。

©コハム