【问题标题】:Flow Types with Promises (Fetch's)带有 Promise 的流类型(Fetch 的)
【发布时间】:2018-05-04 02:40:33
【问题描述】:

我创建了一个 Fetch 函数来使用 JSON API,并为 JSON 对象定义了类型。我对如何定义getCurrentJobAPI 函数的返回类型感到困惑,因为我之后做了一堆.then()。返回值是最后一个 .then() 吗?在我的代码中,最后一个 .then() 是一个 setState,那么它的类型是什么?

    getCurrentJobAPI = (): {} => {

        const url: string = `dummy_url&job_id=${this.props.currentJob}`;

        return fetch(url, {credentials: 'include'})
            .then((response) => {
                return response.json();
            })
            .then((json: CurrentJob) => {
                console.log(json);
                const location = json.inventoryJob.location;
                const ref_note = json.inventoryJob.note;
                const id = json.inventoryJob.id;
                const models = json.inventoryJobDetails.map((j) => {
                    return Object.assign({}, {
                        code: j.code,
                        qty: j.qty
                    })
                });
                this.setState({ currentCodes: models, location: location, ref_note: ref_note, id: id})
                return json
            })
            .then((json: CurrentJob) => {
            const barcodes = json.inventoryJob.history;
            if (barcodes.length > 0) {
                this.setState({apiBarcodes: barcodes})
            }
            this.calculateRows();
            this.insertApiBarcodes();
            this.setState({ initialLoad: true });
            })
    };

更新

虽然我知道我应该将Promise<type> 定义为getCurrentJobAPI 的返回值(参见Gilad 的答案和cmets),但我仍然不确定为什么我不能写Promise<CurrentJob> 如果Fetch 解析为 JSON 响应。

[根据loganfsmyth 的建议,我已经压缩了我的 .then() 语句。]

这里是CurrentJob的类型定义:

type Job = {
    user_id: number,
    status: 'open' | 'closed',
    location: 'string',
    history: {[number]: string}[],
    note: string,
} & CommonCurrentJob;

type JobDetails = {
    iaj_id: number,
    code: number,
} & CommonCurrentJob;


type CommonCurrentJob = {
    id: number,
    qty: number,
    qty_changed: number,
    created_at: string,
    updated_at: string
}

【问题讨论】:

  • 返回值是整个promise链。从技术上讲,您可以获取getCurrentJobAPI() 的结果并附加更多thencatch 类型的东西。那是你问的吗?否则我不知道该说什么。除了 Promise 本身,你不能返回任何东西,否则它将不再是异步的。
  • 如果返回值是整个promise链,如何在Flow中定义Promise的类型?这种情况的文档在哪里?
  • 你不能。这不是异步的工作方式。如果您尝试在promise 链之后 运行代码,它可能会在promise 结束之前运行before。所以你不能认为它是返回一个值,因为那是同步的
  • 抱歉,我才发现我没有理解你的问题。我的坏
  • 不是您问题的答案,但几乎不需要.thens。你可以使用.then 来返回另一个promise,或者作为promise 链中的最终处理程序。大多数中间 .then 处理程序最终都会返回 undefined,因此您可以将它们折叠到第一个 .then

标签: javascript flowtype


【解决方案1】:

首先,免责声明,我是 TypeScript 用户,但我发现这个问题实际上适用于两种语言并且具有相同的答案。

我创建了一个 Fetch 函数来使用 JSON API,并为 JSON 对象定义了类型。我对如何定义 getCurrentJobAPI 函数的返回类型感到困惑,因为之后我做了一堆 .then() 。返回值是最后一个 .then() 吗?在我的代码中,最后一个 .then() 是一个 setState,那么它的类型是什么?

TL;DR:Promise<void>(见注释)。正如您所怀疑的,这实际上是承诺链中最后一个顶级 .then 的返回类型。

现在让我们深入挖掘一下

这是您的示例,稍加修改以利用类型推断,而不是注释回调参数,这些参数被其接收者声明为 any

顺便说一句,这些回调参数注释相当于不安全的隐式转换,或者我们在 TypeScript 中称之为类型断言,它们与代码的形状有关。它们看起来像这样

declare function takesFn(fn: (args: any) => any): void;

所以我尽量减少了这些,因为它们形成了一个微妙的陷阱

// @flow
import React from 'react';

type CurrentJob = {
    inventoryJob: Job,
    inventoryJobDetails: JobDetails[]
}

export default class A extends React.Component<{currentJob:JobDetails}, any> {

  getCurrentJobAPI: () => Promise<void> = () => {

        const url = `dummy_url&job_id=${String(this.props.currentJob)}`;

        return fetch(url, {credentials: 'include'})
            .then(response => {
                return (response : {json(): any}).json();
            }) // --> Promise<any>
            .then(json => {

                const currentJob = (json: CurrentJob); // make the assumption explicit.

                console.log(currentJob);

                const {location, id, note: ref_note} = currentJob.inventoryJob;

                const currentCodes = currentJob.inventoryJobDetails
                  .map(({code, qty}) => ({
                    code,
                    qty
                  }));

                this.setState({currentCodes, location, ref_note, id});
                return currentJob;
            }) // --> Promise<CurrentJob>
            .then(currentJob => {
                const apiBarcodes = currentJob.inventoryJob.history;
                if (apiBarcodes.length > 0) {
                    this.setState({apiBarcodes});
                }
                this.setState({initialLoad: true});
            }); // --> Promise<void>
    };
}

所以我在上面的每个then 调用中对承诺进行断言,但是这些断言都通过类型推断进行验证,除了响应值的初始类型转换。

作为进一步的证据,如果我们从AgetCurrentJobAPI 属性中删除类型声明,流将推断其类型实际上是Promise&lt;void&gt;

奖励:使用async/await 进行简化。我在上面使用了几个 ESNext 功能来缩短代码并使其更愉快,但我们可以利用特定功能 async/await 来更容易理解基于 Promise 的控制流和类型代码。

考虑这个版本。

// @flow
import React from 'react';

type CurrentJob = {
    inventoryJob: Job,
    inventoryJobDetails: JobDetails[]
}

export default class A extends React.Component<{currentJob:JobDetails}, any> {
    getCurrentJobAPI = async () => {

        const url = `dummy_url&job_id=${String(this.props.currentJob)}`;

        const response = await fetch(url, {credentials: 'include'});
        const json = await response.json();
        const currentJob = (json: CurrentJob); // make the assumption explicit.

        console.log(currentJob);

        const {location, id, note: ref_note} = currentJob.inventoryJob;

        const currentCodes = currentJob.inventoryJobDetails.map(({code, qty}) => ({
            code,
            qty
        }));

        this.setState({currentCodes, location, ref_note, id});


        const apiBarcodes = currentJob.inventoryJob.history;
        if (apiBarcodes.length > 0) {
            this.setState({apiBarcodes});
        }
        this.setState({initialLoad: true});

    };
}

很明显,这是一个void 函数。它没有return 语句。但是,作为async 函数,它固有地返回一个Promise,就像写成显式Promise 链时一样。

注意: void 是一种在 Flow 和 TypeScript 中非常有用的构造,用于表示不返回值但实际上此类函数实际上返回 undefined 的函数的语义意图,因为,嗯,这是 JavaScript。 Flow 似乎没有将undefined 识别为一种类型,但在TypeScript 下,该函数同样可以被注释为返回Promise&lt;undefined&gt;。不管怎样,Promise&lt;void&gt; 更可取,因为它提供了清晰的意图。

备注:我使用https://flow.org/try 和 Windows 的流二进制文件的组合解决了这个问题。在 Windows 上的体验真的很糟糕,希望它会有所改善。

【讨论】:

    【解决方案2】:

    当链接 then 时,结果将始终是一个承诺。 调用then 时,返回值是另一个promise,否则不可能链接then。 您可以通过使用围绕整个链的 console.log() 轻松看到这一点。

    【讨论】:

    • 那么我该如何定义函数的返回类型呢?我是否定义了一个包含整个链的类型?
    • 这种情况下的返回类型是 Promise。
    • @AvremelKaminetzky 是的,在 Flow Promise 中,Promise 是一种类型生成类型(即,它以 sa 类型作为参数并在内部生成一个类型基数),因此您必须为 Promise 指定一个类型,例如例如 Promise&lt;string&gt; 用于使用字符串解析的承诺。你也可以使用Promise&lt;*&gt; 让 Flow 自己判断,使用Promise&lt;any&gt; 防止 Flow 检查 Promise 的实际类型(不推荐,any 只能在没有其他选择的情况下使用)。跨度>
    • @AvremelKaminetzky 无论承诺如何解决!当然,这需要你真正了解你的承诺链——即减去类型,只是事物本身?如果你没有完全“得到”承诺,就很难谈论类型。您必须首先了解您的代码。 getCurrentJobAPI 解析承诺链的最后一个元素返回的内容。那个东西的类型就是promise的类型——因为它总是返回一个promise,你只需要找出这个promise用什么来解决。
    • @AvremelKaminetzky JSON 是一种字符串格式。你要么有一个 JSON string(!!!) - 要么你有一个对象。没有“JSON 对象”这样的东西,实际上这个术语本身就是一个矛盾,就像“冰火”一样。您可以将对象编码为 JSON 字符串。但是你有一个字符串。其中包含一个对象,但前提是您对其进行解码。
    猜你喜欢
    • 2018-07-17
    • 2021-07-26
    • 1970-01-01
    • 1970-01-01
    • 2020-04-24
    • 1970-01-01
    • 1970-01-01
    • 2021-05-08
    • 2020-06-01
    相关资源
    最近更新 更多