Promise.try()

基准线 2025
新推出

自 ⁨2025 年 1 月⁩ 起,此功能在最新的设备和浏览器版本中均可正常工作。此功能可能无法在旧设备或浏览器中工作。

Promise.try() 静态方法接受任何类型的回调函数(可以返回值或抛出异常,同步或异步),并将其结果封装在一个 Promise 中。

语法

js
Promise.try(func)
Promise.try(func, arg1)
Promise.try(func, arg1, arg2)
Promise.try(func, arg1, arg2, /* …, */ argN)

参数

func

一个同步调用的函数,并附带提供的参数(arg1, arg2, …, argN)。它可以做任何事情——返回值、抛出异常或返回一个 promise。

arg1, arg2, …, argN

要传递给 func 的参数。

返回值

一个 Promise,它

  • 如果 func 同步返回值,则为已解决。
  • 如果 func 同步抛出异常,则为已拒绝。
  • 如果 func 返回一个 promise,则为异步解决或拒绝。

描述

您可能有一个接受回调函数的 API。该回调函数可能是同步的或异步的。您希望通过将结果封装在 promise 中来统一处理所有内容。最直接的方法可能是 Promise.resolve(func())。问题是,如果 func() 同步抛出异常,该异常将不会被捕获并转换为已拒绝的 promise。

常见的做法(将函数调用的结果提升为 promise,已解决或已拒绝)通常如下所示:

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

Promise.try() 在这里更有帮助

js
Promise.try(func);

对于内置的 Promise() 构造函数,从执行器抛出的异常会自动捕获并转换为拒绝,因此这两种方法在大多数情况下是等效的,只是 Promise.try() 更简洁易读。

请注意,Promise.try() 等同于此,尽管它们非常相似。

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

区别在于传递给 then() 的回调函数始终是异步调用的,而 Promise() 构造函数的执行器是同步调用的。Promise.try 也同步调用函数,并在可能的情况下立即解析 promise。

Promise.try() 结合 catch()finally(),可以用来在单个链中处理同步和异步错误,使 promise 的错误处理看起来几乎像同步错误处理。

setTimeout() 一样,Promise.try() 接受额外的参数,这些参数会传递给回调函数。这意味着,与其这样做

js
Promise.try(() => func(arg1, arg2));

您可以这样做

js
Promise.try(func, arg1, arg2);

这两者是等效的,但后者避免了创建额外的闭包,并且效率更高。

示例

使用 Promise.try()

以下示例接受一个回调函数,“提升”它到 promise,处理结果,并进行一些错误处理

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() 的一个更忠实的近似(尽管它仍然不应该用作 polyfill)

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

由于 Promise.try() 的实现方式(即 try...catch),我们可以安全地将 Promise.try()this 设置为任何自定义构造函数,它永远不会同步抛出异常。

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() 构造函数在运行执行器时*不会*优雅地处理异常。但是,尽管有 throwPromise.try() 仍然会捕获异常,并将其传递给 reject() 进行输出。

规范

规范
ECMAScript® 2026 语言规范
# sec-promise.try

浏览器兼容性

另见