Promise.try()

Limited availability

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

Promise.try() 静态方法接受一个任意类型的回调函数(无论其是同步或异步,返回结果或抛出异常),并将其结果封装成一个 Promise

语法

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

参数

  • func
    • :使用提供的参数(arg1arg2、...、argN)同步调用的函数。它可以做任何事情——要么返回一个值、抛出一个错误或者返回一个 promise。
  • arg1arg2、…、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("完成"));
}

doSomething(() => "同步的结果");

doSomething(() => {
  throw new Error("同步的错误");
});

doSomething(async () => "异步的结果");

doSomething(async () => {
  throw new 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) {
    // “resolve”函数和“reject”函数的行为与原生 promise 完全不同,
    // 但 Promise.try() 只是调用 resolve
    executor(
      (value) => console.log("已解决", value),
      (reason) => console.log("已拒绝", reason),
    );
  }
}

const p = Promise.try.call(NotPromise, () => "哈喽");
// 输出:已解决 哈喽

const p2 = Promise.try.call(NotPromise, () => {
  throw new Error("哦嚯");
});
// 输出:已拒绝 Error: 哦嚯

Promise() 不同,NotPromise() 构造函数在运行执行器期间不会优雅地处理异常。尽管 throw 语句存在,Promise.try() 仍然能捕获异常并将其传递给 reject() 以便记录。

规范

Specification
Promise.try
# sec-promise.try

浏览器兼容性

BCD tables only load in the browser

参见