【问题标题】:Rewrite error catching higher order function to catch async errors?重写错误捕获高阶函数以捕获异步错误?
【发布时间】:2020-10-01 04:24:03
【问题描述】:

这里我有一个函数可以很好地捕捉同步错误,并在重新抛出它们之前对其进行处理。

    function logExceptions<T extends (...args: any[]) => any>(func: T): (...funcArgs: Parameters<T>) => ReturnType<T> {
      return (...args: Parameters<T>): ReturnType<T> => {
        try {
          return func(...args);
        } catch (err) {
          console.log(func.name + " caused an error")
          throw err;
        }
      };
    }

function syncExample() { 
  throw new Error()
}

logExceptions(syncExample)();

控制台日志

“syncExample 导致错误”

我可以重写此函数以使其与异步无关并且也适用于异步函数吗?

async function asyncExample() { 
  throw new Error()
}
logExceptions(asyncExample)();

所需的控制台.log

“asyncExample 导致错误”

实际的控制台日志

""

【问题讨论】:

  • "我可以重写此函数以使其与异步无关并且也适用于异步函数吗?" - 不。你应该为此编写一个单独的函数。

标签: typescript asynchronous async-await functional-programming


【解决方案1】:

我可以重写此函数以使其与异步无关并且也适用于异步函数吗?

没有。虽然您可以尝试重载它并检测该函数是否会返回一个承诺,但这非常脆弱。最好写一个单独的函数来包装异步函数:

function logExceptions<T extends any[], U>(func: (...args: T) => PromiseLike<U>): (...args: T) => Promise<U> {
  return async (...args) => {
    try {
      return await func(...args);
    } catch (err) {
      console.log(func.name + " caused an error")
      throw err;
    }
  };
}

【讨论】:

  • @GrégoryNEUT No
【解决方案2】:

同意@Bergi 关于新功能的看法。


Typescript 不喜欢在使用 async 方法时直接返回 ReturnType。我想这是因为我没有指定ReturnType 必须是Promise 类型,但我现在找到了如何指定它的方法。

type ReturnType any> = T extends (...args: 任何)=> 推断 R ? R : any 获取函数类型的返回类型

异步函数或方法的返回类型必须是全局的 承诺类型。(1064)

通过提取 Promise 中的模板内容并重新声明它,我找到了一个转机。

type ExtractPromiseTemplate<T> = T extends PromiseLike<infer U> ? U : T

function logExceptions<T extends (...args: any[]) => ReturnType<T>>(func: T): (...funcArgs: Parameters<T>) => Promise<ExtractPromiseTemplate<ReturnType<T>>> {
      return async (...args: Parameters<T>): Promise<ExtractPromiseTemplate<ReturnType<T>>> => {
        try {
          console.log('Will call now');
          const ret = await func(...args);

          return ret as ExtractPromiseTemplate<ReturnType<T>>;
        } catch (err) {
          console.log(func.name + " caused an error");

          throw err;
        }
      };
    }

async function asyncExample() { 
  throw new Error('Example')
}

logExceptions(asyncExample)();

调用以下代码测试返回值的有效性:

type ExtractPromiseTemplate<T> = T extends PromiseLike<infer U> ? U : T

function logExceptions<T extends (...args: any[]) => ReturnType<T>>(func: T): (...funcArgs: Parameters<T>) => Promise<ExtractPromiseTemplate<ReturnType<T>>> {
      return async (...args: Parameters<T>): Promise<ExtractPromiseTemplate<ReturnType<T>>> => {
        try {
          console.log('Will call now');
          const ret = await func(...args);

          return ret as Promise<ExtractPromiseTemplate<ReturnType<T>>>;
        } catch (err) {
          console.log(func.name + " caused an error");

          throw err;
        }
      };
    }

async function asyncExample():Promise<string> { 
  return 'a';
}

(async() => {
  const ret = await logExceptions(asyncExample)();
})();


New playground for @bergi comment

【讨论】:

  • 看起来不错。如果将函数重写为仅处理异步函数,是否可以简化解决方案并避免使用 ExtractPromiseTemplate?
  • 这个方法已经只引用了异步函数。问题是,打字稿在简单地返回 ReturnType&lt;T&gt; 时出错,这就是我使用解决方法的原因。如果有人阅读我的答案可以找到另一种方式,那就太好了。我同意它很大。
  • 此外,您不需要将 ret 转换为其他类型,它应该可以正常工作 - 请注意 ret 不是承诺。
  • This 可以工作,但很丑(而且稍微不正确),因为它需要演员表。
  • @GrégoryNEUT 现在发布了一个答案,只需使用两个类型参数即可正确完成
猜你喜欢
  • 2021-03-08
  • 1970-01-01
  • 1970-01-01
  • 2021-10-20
  • 1970-01-01
  • 2019-11-26
  • 2014-12-01
  • 1970-01-01
  • 2020-09-12
相关资源
最近更新 更多