在我的 JavaScript 数据加载承诺中,我需要什么 `setTimeout(resolve)`?

What do I need `setTimeout(resolve)` in my JavaScript data-loading promise?

我正在尝试理解 ES6 Promises,不仅是从 howto 文章中获取简单的示例代码来工作,而且实际上是在尝试将学到的概念实现到代码中,这些代码可能在实际项目中以某种方式有用。

下面我在前端 Vue 中创建了代码。js/axios 它成功地使用 Promises 将数据加载到屏幕的五个区域。在我的后端 loadData 操作中,我让它人为地等待一秒钟,这样我就可以看到屏幕的五个区域中的每一个区域以一秒的间隔加载数据:

<div id="app" class="pageContent">
    <div>Data 1: {{data1}}</div>
    <div>Data 2: {{data2}}</div>
    <div>Data 3: {{data3}}</div>
    <div>Data 4: {{data4}}</div>
    <div>Data 5: {{data5}}</div>
</div>
<script>
    const app = new Vue({
        el: '#app',
        data: {
            data1: 'loading...',
            data2: 'loading...',
            data3: 'loading...',
            data4: 'loading...',
            data5: 'loading...'
        },
        methods: {
            initialize: function () {
                const that = this;
                this.loadData('data1')
                    .then(() => {
                        return that.loadData('data2');
                    })
                    .then(() => {
                        return that.loadData('data3');
                    })
                    .then(() => {
                        return that.loadData('data4');
                    })
                    .then(() => {
                        return that.loadData('data5');
                    });
            },
            loadData: function (idCode) {
                return new Promise((resolve, reject) => {
                    const that = this;
                    axios({
                        method: 'post',
                        url: 'controllerShowcaseLoadDataWithPromises',
                        data: {
                            action: 'loadData',
                            idCode: idCode
                        }
                    }).then(function (responseObject) {
                        const response = qsys.getResponse(responseObject);
                        that[idCode] = response.data.message;
                        setTimeout(resolve);
                    });
                });
            },
        }
    });
    app.initialize();

虽然这似乎工作正常,但谁能解释为什么我必须使用 setTimeout(resolve)(没有它,代码只执行一次)以及这里实际发生了什么?我知道这基本上是 execute a callback 的方式,就像我们在 promises 之前所做的那样,但是什么是更干净、更标准的方式来做到这一点?

一个'cleaner'做你现在正在做的事情的方法是将所有的承诺添加到一个数组中,并使用Promise.all:

const requests = [
    that.loadData('data1'),
    that.loadData('data2'),
    that.loadData('data3'),
    that.loadData('data4'),
    that.loadData('data5')
];

Promise.all(requests).then(value => {
    // All resolved values in order
}).catch(error => {
    // Error
});

你的 loadData 函数在你没有 resolve 承诺时只执行一次的原因正是因为你没有解决承诺。 then 永远不会在您的链中执行。

无需将 resolve 包裹在 setTimeout 中。

...why I have to use setTimeout(resolve)

您不必使用 setTimeout,但您必须调用 resolve(或 reject)才能兑现承诺。

what is actually going here?

您正在执行一系列异步调用,等待前一个完成后再执行下一个,因为您将承诺链接在一起的方式。这就是为什么如果您不解决第一个问题,none 其他人会运行:您已经链接了您的操作,以便在每种情况下,下一个仅在前一个操作成功完成时运行。如果您不调用 resolve,尽管操作已完成,但您永远不会兑现您创建的承诺,让使用该承诺的代码完成操作。

I understand that this is the basically the way to execute a callback as we used to do it before promises, but what is a cleaner, more standard way to do this?

如果必须显式创建承诺,调用 resolve 是标准方法。 但是,在这种情况下你不必这样做,因为你已经有来自axios的承诺。所以 loadData 应该是:

loadData: function(idCode) {
    return axios({
        method: 'post',
        url: 'controllerShowcaseLoadDataWithPromises',
        data: {
            action: 'loadData',
            idCode: idCode
        }
    }).then(function (responseObject) {
        const response = qsys.getResponse(responseObject);
        this[idCode] = response.data.message;
    });
},

请注意,它 returns 是根据 axios 的承诺调用 then 的结果。 (另请注意,由于您使用的是箭头函数,因此不需要 that = this 东西。箭头函数 关闭 this。)

如果你想一个接一个地完成你的操作,那么你的承诺链就是你如何做到这一点,尽管如果你愿意,你可以把它写得更简洁,你应该处理错误(再说一遍,不需要 that = this):

this.loadData('data1')
    .then(() => this.loadData('data2'))
    .then(() => this.loadData('data3'))
    .then(() => this.loadData('data4'))
    .then(() => this.loadData('data5'))
    .catch(error => {
        // handle/report error here
    });

如果你想并行(重叠)完成这五个操作,你可以立即开始它们并使用Promise.all等待它们全部完成:

Promise.all([
    this.loadData('data1'),
    this.loadData('data2'),
    this.loadData('data3'),
    this.loadData('data4'),
    this.loadData('data5'),
])
.catch(error => {
    // handle/report error here
});

最后,根据您的目标浏览器以及是否要转译,您还可以使用 async 函数和 await:

loadData: async function(idCode) {
    const responseObject = await axios({
        method: 'post',
        url: 'controllerShowcaseLoadDataWithPromises',
        data: {
            action: 'loadData',
            idCode: idCode
        }
    });
    const response = qsys.getResponse(responseObject);
    this[idCode] = response.data.message;
},