为啥 Typescript 认为 async/await 返回包装在承诺中的值?

Posted

技术标签:

【中文标题】为啥 Typescript 认为 async/await 返回包装在承诺中的值?【英文标题】:Why does Typescript think async/await returns a value wrapped in a promise?为什么 Typescript 认为 async/await 返回包装在承诺中的值? 【发布时间】:2018-03-29 15:07:14 【问题描述】:

我想将一个 Promise 链重构为 async/await,但 Typescript 抱怨打字。

TS2322:类型“IHttpPromiseCallbackArg”不可分配给类型“IResp”...

我认为await 会返回一个常规值,而不是一个承诺。我错了吗?如果是这样,我如何分配类型以便编译所需的代码?

我认为 await 会返回与 .then 回调中的第一个参数相同的值。我错了吗?

旧代码:

handleSubmit(form) 
    const params = this.getParams(form);

    this.myAsyncRequest(params)
       .then((resp:IResp) => this.processResp(resp))
       .catch((e:IServerError) => this.handleError(e));
 

所需的新代码:

async handleSubmit(form) 
    const params = this.getParams(form);

    try 
        const resp:IResp = await this.myAsyncRequest(params); //typing error with "const resp:IResp"
        this.processResp(resp);
     catch (e:IServerError) 
        this.handleError(e);
    

如果我删除 myAsyncRequest 中的返回类型,所需的代码仍然会中断;我猜 Typescript 直接从 AngularJS 库中推断出来的。

myAsyncRequest(params:IParams):IHttpPromise<IResp> 
    return $http.post('blah', data);

如果我从 const resp 声明中删除“IResp”,则 processResponse 会抱怨 IHttp 不等于 IResp...

processResp(resp:IResp) 
    //do stuff

【问题讨论】:

IHttpPromiseCallbackArg 类型不是承诺。这已经是函数调用的实际结果了。见definitelytyped.org/docs/angular-translate--angular-translate/… 我可以将我的问题改写为 - “我认为 await 会返回与 .then 回调中的第一个参数相同的值。我错了吗?” 你是对的。但是你对 then 回调函数中第一个参数的类型是错误的。见definitelytyped.org/docs/angularjs--angular-route/interfaces/… 也许您对实际使用的类型定义错误。但是我们需要一个完整的工作示例来明确地说明这一点。 【参考方案1】:

包含await 的行确实等待解析的值 - 但由于函数本身是异步的(允许所有等待),你会得到一个承诺。

示例...在下面的代码中,您可以将x 用作纯数字(即使delay 返回一个承诺)并再次用于y - 因此您等待的所有内容都已解决,因此您可以使用如果它是同步的,它更像你会的。

看起来不像它的异步函数返回了一个承诺,现在可以了。

这似乎令人困惑,因为它似乎“颠倒了承诺”,但它所做的是将then 转移到顶层(您可以让异步函数调用其他异步函数等等)。

function delay(ms: number) 
    return new Promise<number>(function(resolve) 
        setTimeout(() => 
            resolve(5);
        , ms);
    );



async function asyncAwait() 
    let x = await delay(1000);
    console.log(x);

    let y = await delay(1000);
    console.log(y);

    return 'Done';


asyncAwait().then((result) => console.log(result));

【讨论】:

所以说到底,我还是得在某个地方打电话给.then 可能仍然必须在最顶部调用then(在某些情况下,您将函数传递给其他东西,您不必这样做) . 反对票是怎么回事?有什么改进答案的建议吗? 这并没有回答似乎对函数返回的类型有误解的问题。 IHttpPromiseCallbackArg 类型不是提问者所假设的承诺。 我试图回答“我认为 await 会返回一个常规值,而不是一个承诺。我错了吗?”【参考方案2】:

您的问题“我认为 await 会返回与 .then 回调中的第一个参数相同的值。我错了吗?”。

不,你完全正确。但是您对 .then 回调中的第一个参数是错误的。

您将myAsyncRequest 定义为返回IHttpPromise&lt;IResp&gt;。但是IHttpPromise&lt;T&gt;被定义为继承IPromise,方式如下:

type IHttpPromise<T> = IPromise<IHttpPromiseCallbackArg<T>>;

因此,IHttpPromise&lt;T&gt; 是一个返回 IHttpPromiseCallbackArg&lt;T&gt; 的承诺,其中 T 类型的实际数据位于 dataIHttpPromiseCallbackArg&lt;T&gt; 属性中。

所以,我们在问题中看到的旧代码变体:

handleSubmit(form) 
    const params = this.getParams(form);

    this.myAsyncRequest(params)
       .then((resp:IResp) => this.processResp(resp))
       .catch((e:IServerError) => this.handleError(e));
 

myAsyncRequest 被定义为返回 IHttpPromise 时,实际上不应该在 TypeScript 中正确编译。

如何解决这个问题:

async handleSubmit(form) 
    const params = this.getParams(form);

    try 
        const httpResp:IHttpPromiseCallbackArg<IResp> = await this.myAsyncRequest(params); 
        const resp: IResp = httpResp.data;
        this.processResp(resp);
     catch (e:IServerError) 
        this.handleError(e);
    


注意:在最新的 Angular 类型定义中,IHttpPromiseCallbackArg&lt;T&gt; 类型实际上称为IHttpResponse&lt;T&gt;

也许在您的代码中,您已将IResp 定义为IHttpResponse&lt;Something&gt;?那么你只是与旧名称IHttpPromiseCallbackArg 发生冲突。然后从DefinitelyTyped 获取使用新名称的最新类型定义。您还必须将myAsyncRequest 的定义更改为:

myAsyncRequest(params:IParams):IHttpPromise<Something> 

【讨论】:

以上是关于为啥 Typescript 认为 async/await 返回包装在承诺中的值?的主要内容,如果未能解决你的问题,请参考以下文章

为啥 TypeScript 中没有重载的构造函数实现?

重构:从Promise到Async/Await

Task.Run 在 ASP .NET MVC Web 应用程序中被认为是不好的做法吗?

Async / await vs then 哪个最适合性能?

js异步回调Async/Await与Promise区别 新学习使用Async/Await

为啥 TypeScript 接受值作为数据类型?