在 TypeScript 中使用 Promises 的异步函数的正确错误模式

Posted

技术标签:

【中文标题】在 TypeScript 中使用 Promises 的异步函数的正确错误模式【英文标题】:Proper error pattern for async functions using Promises in TypeScript 【发布时间】:2017-06-11 07:43:08 【问题描述】:

我想创建一个具有正确错误处理的类型化异步函数。

我可以这样定义:

export async function doSomething(userId:string) : Promise<ISomething | void> 

    let somthing: ISomething = ;

    try 
        something.user = await UserDocument.findById(userId);
        something.pet = await PetDocument.findOne(ownerId:userId);
        return Promise.resolve(something);
     catch (err)
        console.log("I would do some stuff here but I also want to have the caller get the error.");
        return Promise.reject(err);
    

...这似乎可行,但是(原因很清楚),如果我尝试将结果分配给ISomething 对象,我会收到错误Type 'void | ISomething' is not assignable to type 'ISomething'

let iSomething:ISomething;
iSomething = await doSomething('12'); //this give me the error

我明白这是为什么。我的问题是,在这种情况下,我应该使用什么模式进行错误处理?请注意,如果返回类型是 Promise&lt;IProfile&gt;,那么我会收到 return Promise.reject(err); 行的错误(这将返回 Profile&lt;void&gt;)。

我可以使用throw err; 代替return Promise.reject(err); 行,但有时我想使用Promise.reject 模式(例如,如果我想在返回之前做更多事情)。

我感觉我在 Promise / async 方面遗漏了一些东西,但我找不到遵循这种模式的类型示例。

...请注意,如果我使用完整的 Promise 模式,它可以正常工作:

doSomething('12')
  .then( (something) => //do stuff)
  .catch( (err) => //handle error);

我应该只使用throw 而忘记Promise.reject 吗?如果我使用throw.catch() 会被正确触发吗?

【问题讨论】:

...我已经回答了我的部分问题:“如果我使用 throw 会适当地触发 .catch() 吗?” - 是的,编写一些测试代码表明确实如此。我猜抛出的错误会变成异步函数中被拒绝的承诺。不过,我仍然很想知道如何在这种情况下使用 Promise.reject (特别是我的打字错误问题)。 重点是可以使用throw。请使用throw,因为您所拥有的模式非常混乱。 我不同意它是模式的混合 - 必须使用 throw 以及返回承诺似乎更像是模式的混合。使用 Promise.resolve 和 Promise.reject 似乎是对模式的一致使用。 【参考方案1】:

首先不返回承诺是我通常实现异步等待模式的方式:

export async function doSomething(userId:string) : Promise<ISomething>

    let something: ISomething = ;

    try 
        something.user = await UserDocument.findById(userId);
        something.pet = await PetDocument.findOne(ownerId:userId);
        return something;
     catch (err)
        console.log("I would do some stuff here but I also want to have the caller get the error.");
        throw(err);
    

因此,如果您不必进行中间清理,则可以将其简化为:

export async function doSomething(userId:string) : Promise<ISomething>    
    let something: ISomething = ;
    something.user = await UserDocument.findById(userId);
    something.pet = await PetDocument.findOne(ownerId:userId);
    return something;

并且拥有

后续等待的函数捕获异常或

调用函数处理被拒绝的承诺

【讨论】:

【参考方案2】:

如果您不想使用 Promise.rejectPromise.resolve,这一切正常,但如果您这样做了 - 这是一个简单的解决方法。

去掉签名中的| void,将拒绝返回改为return Promise.reject&lt;ISomething&gt;(err);。有效!

这是问题的修改版本:

export async function doSomething(userId:string) : Promise<ISomething> 

    let something: ISomething = ;

    try 
        something.user = await UserDocument.findById(userId);
        something.pet = await PetDocument.findOne(ownerId:userId);
        return Promise.resolve(something);
     catch (err)
        console.log("I would do some stuff here but I also want to have the caller get the error.");
        return Promise.reject<ISomething>(err);
    

【讨论】:

这是对编译器的谎言,因为Promise.reject 没有返回ISomething 类型的东西。

以上是关于在 TypeScript 中使用 Promises 的异步函数的正确错误模式的主要内容,如果未能解决你的问题,请参考以下文章

TypeScript VS - 编译错误但没有设计时错误

我期望从 API 响应中接收一组对象,但在使用 TypeScript 的 React 中获得一组 Promise

Learining TypeScript TypeScript 简介

Learining TypeScript TypeScript 简介

TypeScript 泛型:如果 Promise 拒绝和解析类型不同怎么办?

关于用 Angular 和 typescript 链接 $q 承诺的困惑