Promise.try()

Limited availability

This feature is not Baseline because it does not work in some of the most widely-used browsers.

Experimental: これは実験的な機能です。
本番で使用する前にブラウザー互換性一覧表をチェックしてください。

Promise.try() 静的メソッドは、あらゆる種類のコールバック(返す、発生する、同期的、非同期的)を受け取り、その結果を Promise でラップします。

構文

js
Promise.try(func)

引数

func

引数なしで同期的に呼び出される関数です。何らかの処理を行うことができ、返値を返すことも、エラーを発生させることも、プロミスを返すことも可能です。

返値

Promise で、次のようなものです。

  • func が同期的に値を返す場合は、すでに履行されたもの。
  • func が同期的にエラーを発生した場合は、すでに拒否されたもの。
  • 非同期で履行または拒否された場合、func はプロミスを返します。

解説

コールバックを受け取る API もあります。 コールバックは同期または非同期のどちらでも可能です。 結果をプロミスでラップすることで、すべてを統一的に処理したい場合、最もわかりやすい方法は、Promise.resolve(func()) でしょう。 問題は、func() で同期敵にエラーが発生した場合、このエラーが補足されず、拒否されたプロミスに変換されないことです。

一般的な手法(履行されたか拒否されたかに関わらず、関数呼び出しの結果をプロミスに持ち上げる)は、以下のようにすることが多いです。

js
new Promise((resolve) => resolve(func()));

しかし、Promise.try() はもっと便利です。

js
Promise.try(func);

組み込みの Promise() コンストラクターでは、実行時に発生したエラーは自動的に捕捉され、拒否に変換されます。そのため、これらの 2 つの手法はほぼ同等ですが、Promise.try() の方がより簡潔で読みやすい点が異なります。

なお、Promise.try() はこれと非常に似ていますが、同等ではありません。

js
Promise.resolve().then(func);

違いは、then() に渡されたコールバックは常に非同期で呼び出されるのに対し、Promise() コンストラクターの実行は同期で呼び出されることです。 Promise.try も関数を同期で呼び出し、可能であれば即座にプロミスを解決します。

Promise.try()catch() および finally() と組み合わせて使用することで、単一の連鎖で同期的および非同期的なエラーの両方を処理することができ、プロミスエラー処理を同期的エラー処理とほとんど同じように見せることができます。

Promise.try() の使用

次の例では、コールバックをプロミスに「引き上げ」、結果を処理し、エラー処理を行います。

js
function doSomething(action) {
  return Promise.try(action)
    .then((result) => console.log(result))
    .catch((error) => console.error(error))
    .finally(() => console.log("Done"));
}

doSomething(() => "Sync result");

doSomething(() => {
  throw new Error("Sync error");
});

doSomething(async () => "Async result");

doSomething(async () => {
  throw new Error("Async error");
});

async/await では、同じコードは次のようになります。

js
async function doSomething(action) {
  try {
    const result = await action();
    console.log(result);
  } catch (error) {
    console.error(error);
  } finally {
    console.log("Done");
  }
}

Promise 以外のコンストラクターにおける try() の呼び出し

Promise.try() は汎用メソッドです。これは、Promise() コンストラクターと同じシグネチャを実装する任意のコンストラクターで呼び出すことができます。

以下は、実際の Promise.try() にやや忠実な近似表現です(ただし、やはりこれはポリフィルとして使用すべきではありません)。

js
Promise.try = function (func) {
  return new this((resolve, reject) => {
    try {
      resolve(func());
    } catch (error) {
      reject(error);
    }
  });
};

Promise.try() の実装方法(つまり、try...catch)により、this を任意のカスタムコンストラクターに設定して Promise.try() を安全に呼び出すことができ、同期してエラーが発生することはありません。

js
class NotPromise {
  constructor(executor) {
    // The "resolve" and "reject" functions behave nothing like the native
    // promise's, but Promise.try() just calls resolve
    executor(
      (value) => console.log("Resolved", value),
      (reason) => console.log("Rejected", reason),
    );
  }
}

const p = Promise.try.call(NotPromise, () => "hello");
// Logs: Resolved hello

const p2 = Promise.try.call(NotPromise, () => {
  throw new Error("oops");
});
// Logs: Rejected Error: oops

Promise() とは異なり、この NotPromise() コンストラクターは、executor を実行する際に例外を適切に処理しません。しかし、throwが発生しても、Promise.try() は例外を捕捉し、それを reject() に渡してログ出力します。

仕様書

Specification
Promise.try
# sec-promise.try

ブラウザーの互換性

BCD tables only load in the browser

関連情報