【问题标题】:Optional catch in javascript promisesjavascript 承诺中的可选捕获
【发布时间】:2018-07-29 05:11:35
【问题描述】:

以下内容有效:

new Promise<void>((resolve, reject) => {
      reject()
    })
    .then(() => {})
    .catch(() => {})

但我可能并不总是关心错误。有没有办法让 catch 成为可选的?

我试过了,但没用:

new Promise<void>((resolve, reject?) => {
      if (reject) reject()
    })
    .then(() => {})

Error: Uncaught (in promise): undefined

【问题讨论】:

  • 是什么阻止你不打电话给catch
  • 当你试图拒绝一个承诺时,它需要一个catch,除非承诺总是返回一个resolve
  • Promise 构造函数将总是被传递一个resolve 和reject 方法——如果你不想捕捉,那么不要.catch(....)
  • 如果我运行第二个示例,我会得到Error: Uncaught (in promise): undefined
  • 嗯,你有未处理的承诺拒绝。 javascript 引擎在发生这种情况时告诉您是正确的。这取决于您所说的“我不在乎错误”是什么意思。您是要终止进程还是默默地忽略错误。如果您想默默地忽略错误,请考虑首先不要调用拒绝 - 或者像在您的第一个 sn-p 中那样捕获并吃掉异常。

标签: javascript typescript promise es6-promise


【解决方案1】:

有没有办法让 catch 成为可选的?

没有。如果您使用的 Promise 可能会出错,您需要处理它(或将其传播给您的调用者)。

当然,如果您自己创建 Promise,拒绝它是可选的,您可以选择从不拒绝您的 Promise,这样您就不需要处理任何错误。但是如果你正在使用的 Promise 有错误,并且你想忽略它们,你必须明确地这样做。随便写

somePromise.catch(e => void e);
// or             () => { /* ignore */ }
// or             function ignore() {}

【讨论】:

  • 虽然不是我希望的答案 ;) ,但它对我的问题来说是最准确的。
【解决方案2】:

我试图解决同样的问题,最后想出了以下承诺包装器:

/**
 * wraps a given promise in a new promise with a default onRejected function,
 * that handles the promise rejection if not other onRejected handler is provided.
 *
 * @param   customPromise      Promise to wrap
 * @param   defaultOnRejected  Default onRejected function
 * @returns wrapped promise
 */
export function promiseWithDefaultOnRejected(customPromise: Promise<any>, defaultOnRejected: (_: any) => any): Promise<any> {

  let hasCatch = false;

  function chain(promise: Promise<any>) {
    const newPromise: Promise<any> = new Promise((res, rej) => {
      return promise.then(
        res,
        function(value) {
          if (hasCatch) {
            rej(value);
          } else {
            defaultOnRejected(value);
          }
        },
      );
    });

    const originalThen = newPromise.then;
    
    // Using `defineProperty` to not overwrite `Promise.prototype.then`
    Object.defineProperty(newPromise, 'then', {
      value: function (onfulfilled: any, onrejected: any) {
        const result: Promise<any> = originalThen.call(newPromise, onfulfilled, onrejected);
        if (typeof onrejected === 'function') {
          hasCatch = true;
          return result;
        } else {
          return chain(result);
        }
      }
    });

    return newPromise;
  }

  return chain(customPromise);
}

这个函数让你用defaultOnRejected 函数包装你的promise,如果没有提供其他处理程序,它将处理被拒绝的promise。例如:

const dontCare = promiseWithDefaultOnRejected(Promise.reject("ignored"), () => {});

结果promise永远不会抛出“Unhandled Promise Rejection”,可以如下使用:

dontCare.then(x=>console.log("never happens")).catch(x=>console.log("happens"));

dontCare.then(x=>console.log("never happens"), x=>console.log("happens"));

或者干脆没有onRejected处理程序:

dontCare.then(x=>console.log("never happens")).then(x=>console.log("also never happens"));

此实用程序的一个问题是它无法按预期使用 async/await 语法工作:您需要按如下方式传播和处理“catch”路径:

async () => {
  try {
    await promiseWithDefaultOnRejected(Promise.reject("ignored"), () => {})
      .catch((e) => { throw e; });
  } catch (e) {
    console.log("happens");
  }
}

【讨论】:

    【解决方案3】:

    当错误是您不关心的问题时,您可以解决。如果您的 catch 返回的不是被拒绝的承诺,则该错误不会沿链传播。

    const ignorableError = new Error("I don't care about this error");
    
    const myPromise = new Promise((resolve, reject) => {
          reject(ignorableError);
        })
        .then(() => {})
        .catch(error => {
          if(error == ignorableError) {
            console.log("Error ignored");
            return;
          }
    
          // Do something else...
    
        });
        
    myPromise.then(() => console.log("Success"))

    【讨论】:

    • 我的问题是我希望我的承诺的用户决定他们是否想抓住。假设您的意思是我应该为忽略错误(在我的情况下,所有错误)的包装器执行上述操作,这是最接近我的解决方案的,所以我会选择这个答案
    • 感谢您在我这样无视承诺时提醒我兑现承诺……我忘了 ;)
    • 不,您不需要返回已履行的承诺。你只需要不抛出也不返回被拒绝的承诺。事实上,你可以不返回任何东西。
    • @tiagodws 刚刚意识到这总是运行.then(),这不是我想要的。 .then() 被捕获时是否可以不运行?我的实验表明不是..
    • @ovg 我不明白你的用例......你不想让它抛出,但也不想解决它。如果你有一个错误,只是简单地忽略或沉默它是一种不好的做法,这就是为什么你首先得到Error: Uncaught (in promise)......出了点问题,引擎只是确保你知道它,但这并没有这并不意味着您的应用程序不工作,只是意味着某些东西没有正确设计(在您的应用程序逻辑或您正在使用的 API 上)。也许解释一下你的情况,我们可以考虑一个更好的解决方案。
    【解决方案4】:

    让我试着描述一下你的情况:

    您有一个获取用户信息的服务和一个使用该服务的名为getUser 的函数。当服务因任何原因失败时,getUser 没有可用的用户。 getUser 的结果在您的代码中使用了很多次,有以下情况:

    1. 用户可以运行函数(代码块)。
    2. 用户无法运行函数(代码块)。
    3. 使用服务的错误/拒绝运行函数。

    当使用getUser 结果时,您可能希望运行所有 3 个函数,可以是其中 2 个函数的组合,也可以只运行一个函数。

    Current getUser 返回一个 Promise,这种类型似乎不适合您的情况。主要是因为拒绝了一个 Promise 而没有抓住它会导致未处理的 Promise 拒绝。而且因为如果你想在用户可用或不可用的情况下运行代码,它会使函数复杂化(它们都必须检查结果而不是假设用户可用或不可用)。

    也许您可以尝试以下代码,请小心在not available 块中做出假设,这可能是由于任何错误。例如:这并不意味着用户不存在,因为它可能是网络错误。

    在示例中使用了getUser,但可以是任何返回承诺的函数,您在拒绝时假设not available

    const isAvailable = promise => {
      //do not expose NOT_AVAILABLE outside this function
      const NOT_AVAILABLE = {type:"not available"};
      //no uncaught promise rejection errors by VM
      const savePromise = promise.catch(x=>x);
      return {
        available:fn=>
          promise
          .catch(e=>Promise.reject(NOT_AVAILABLE))
          .then(fn)
          .catch(
            e=>
              (e===NOT_AVAILABLE)
                ? undefined//ignore
                : Promise.reject(e)//re throw, error is not from service
          ),
        //call not available with the promise if promise rejects
        notAvailable:fn=>promise.catch(()=>fn(promise)),
        catchError:promise.catch.bind(promise)
      };
    }
    
    const service = arg =>
      (arg===1)
        ? Promise.resolve(arg)
        : Promise.reject(arg)
    
    const getUser = arg => isAvailable(service(arg));
    
    var user = getUser(2);
    //if service failed available will be skipped
    user.available(
      user=>console.log("skipped:",user)
    );
    //both catch and notAvailable will be called
    user.notAvailable(
      arg=>console.log("not available:",arg)
    );
    user.notAvailable(
      arg=>console.log("still not available:",arg)
    );
    //not handling catchError does not cause uncaught promise exception
    //  but you can inspect the error
    // user.catchError(
    //   err=>console.log("error is::",err)
    // );
    
    
    var user = getUser(1);
    //you can call available on user multiple times
    user.available(
      user=>console.log("got user:",user)
    );
    user.available(
      user=>Promise.resolve(user)
      .then(user=>console.log("still got user:",user))
      .then(x=>Promise.reject("have to catch this one though"))
      .catch(e=>console.log("ok, got error trying to run available block:",e))
    );
    
    //showing you can inspect the error
    var user = getUser(5);
    user.catchError(err=>console.log("error from service:",err));

    【讨论】:

    • 这就是我想要的:dontCare(Promise.reject("ignored")).then(x=&gt;console.log("never happens")).catch(x=&gt;console.log("happens"));
    • 同时仍然允许dontCare(Promise.reject("ignored")).then(x=&gt;console.log("never happens"))
    • 因此,为客户端制作 可选 ;)
    • @Bergi 指出这是不可能的。但是我可以使用您的解决方案来创建一个包装器,让客户选择他们是否想要一个可捕获的承诺(但这比我在这个问题中寻找的解决方案更臃肿)
    • @ovg 更新了答案,我认为 promise 不是您想做的正确类型,但也许答案可以让您了解您的选择。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2022-11-11
    • 2021-11-06
    • 2017-11-24
    • 1970-01-01
    • 2021-11-15
    • 2016-06-15
    • 1970-01-01
    相关资源
    最近更新 更多