重写错误捕获高阶函数以捕获异步错误?

时间:2020-06-11 13:44:40

标签: typescript asynchronous async-await functional-programming

这里,我有一个功能很好,可以捕获同步错误,并在重新抛出错误之前对其进行处理。

    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)();

console.log

“ syncExample导致错误”

我可以重写此功能以使其成为不可知论者,并且还可以用于异步功能吗?

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

所需的console.log

“ asyncExample导致错误”

实际console.log

“”

2 个答案:

答案 0 :(得分:2)

我可以重写此功能以使其成为不可知论者,并且还可以用于异步功能吗?

不。尽管您可以尝试重载它并检测该函数是否返回诺言,但这是非常脆弱的。最好编写一个单独的函数来包装异步函数:

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;
    }
  };
}

答案 1 :(得分:1)

就新功能与@Bergi达成协议。


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

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

异步函数或方法的返回类型必须是全局的 承诺类型。(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