Javascript 承诺:Return 'In Progress' 响应直到承诺解决?

Javascript Promise: Return 'In Progress' Response Until Promise Resolves?

这不是真正的 Apollo 问题,它是一个 Javascript promises 问题,但使用了 Apollo 的示例,因为那是我记得唯一一次看到它。

Apollo 有一个 React 钩子,看起来像 this:

const { loading, error, data } = useQuery(GET_DOGS);

我明白它是如何 returns error -- 如果承诺解析器抛出错误,你会得到一个错误。

我了解它是如何 returns data -- 当承诺解析器完成时,它 returns 数据。

但是如何 return loading 然后 return data?我已经编写了很多 node.js promise 解析器,但还没有看到一个模式可以 return loading 在操作过程中,然后 return data.

什么 Javascript 模式使这成为可能?

他们会使用一个从 true 开始并在完成后切换到 false 的状态变量,大概是这样的:

function useQuery(/*...*/) {
    const [loading, setLoading] = useState(true);
    const [error, setError] = useState(null);
    const [data, setData] = useState(null);

    useEffect(() => {
        let cancelled = false;
        goGetTheStuff()
        .then(data => {
            if (!cancelled) {
                setData(data);
                setLoading(false);
            }
        })
        .catch(error => {
            if (!cancelled) {
                setError(error);
                setLoading(false);
            }
        });
        return () => {
            cancelled = true;
        };
    }, []);

    return {loading, error, data};
}

实例:

const {useState, useEffect} = React;

function goGetTheStuff() {
    return new Promise((resolve, reject) => {
        setTimeout(() => {
            if (Math.random() < 0.7) {
                // Emulate success
                resolve({data: "here"});
            } else {
                // Emulate failure
                reject(new Error("Couldn't get the data"));
            }
        }, 800);
    });
}

function useQuery(/*...*/) {
    const [loading, setLoading] = useState(true);
    const [error, setError] = useState(null);
    const [data, setData] = useState(null);

    useEffect(() => {
        let cancelled = false;
        goGetTheStuff()
        .then(data => {
            if (!cancelled) {
                setData(data);
                setLoading(false);
            }
        })
        .catch(error => {
            if (!cancelled) {
                setError(error);
                setLoading(false);
            }
        });
        return () => {
            cancelled = true;
        };
    }, []);

    return {loading, error, data};
}

function Example() {
    const {loading, error, data} = useQuery();
    return (
        <div>
            <div>loading: {JSON.stringify(loading)}</div>
            <div>data: {data && JSON.stringify(data)}</div>
            <div>error: {error && error.message}</div>
        </div>
    );
}

ReactDOM.render(<Example/>, document.getElementById("root"));
<div>70% of the time when you run this, the async operation succeeds; 30% of the time, it fails. Run repeatedly if you want to see both scenarios.</div>
<hr>
<div id="root"></div>

<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.13.0/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.13.0/umd/react-dom.production.min.js"></script>