【问题标题】:Javascript promises stuck waiting for each otherJavascript 承诺卡在等待对方
【发布时间】:2017-12-08 02:54:14
【问题描述】:

我有这个用于管理仪表板的代码,其中包含大约 100 个独立检查。

通过 AJAX 调用接收检查结果。

在开始时,每项检查都有一个初始请求。在收到特定检查的结果后,代码递归等待设置超时并再次重复该检查的请求。

一个承诺 = 一个检查。

我想知道为什么 Promise 仅在 每个都处于待处理状态(它们都没有处于超时期限)之后才开始解决。即使来自服务器的响应是“即时的”,它们也只是等待循环中的最后一个承诺。

const TIMEOUT = 4000;

function checkForUpdate(environment, application, check) {
    Dashboard.setCheckPending(environment, application, check);

    return Communicator.getStatus(environment, application, check)
        .then(status => {
            Dashboard.updateCheckCell(environment, application, check, status);
            Dashboard.updateEnvironmentCell(environment, application);

            setTimeout(() => {
                    return checkForUpdate(environment, application, check)
                },
                TIMEOUT
            );
        });
}


Communicator.getEnvMatrix()
    .then(data => {
        Dashboard.create(data);

        $.each(data, (environment, applications) => {
            $.each(applications, (application, checks) => {
                $.each(checks, (key, check) => {
                    checkForUpdate(environment, application, check);
                });
            });
        });
    });

问题还在于如何重写它,以便每个检查只等待自己的结果被传递并设置超时。


编辑(澄清):

100 个检查中的每一个都是独立的,这就是为什么我想尽快为每个检查运行 AJAX(在 $.each() 循环内)。

检查仅依赖于自身。我不希望它等待任何其他检查。

收到检查结果后,它必须等待设置的超时时间才能再次尝试检索其状态。这就是我将递归函数封装在setTimeout() 中的原因。

即使我重写(见下文)setTimeout() 作为承诺,不幸的是行为保持不变。

function delay(timeout) {
    return new Promise(resolve => {
        setTimeout(resolve, timeout);
    });
}


function checkForUpdate(environment, application, check) {
    Dashboard.setCheckPending(environment, application, check);

    let promise = Communicator.getStatus(environment, application, check).promise();

    return promise
        .then(status => {
            Dashboard.updateCheckCell(environment, application, check, status);
            Dashboard.updateEnvironmentCell(environment, application);

            return delay(TIMEOUT).then(() => {
                return checkForUpdate(environment, application, check);
            });
        });
}

【问题讨论】:

  • 为什么不用resolve()代替Timeout
  • 你能更好地描述这段代码应该完成什么吗?这里有几处错误,但我不太理解这段代码的目标是知道如何提出适当的替代方案。例如,您在超时后调用checkForUpdate(),但这完全独立于所有先前的承诺,因此它与任何这些都无关,而且您似乎永远都在这样做。这真的是你想要的吗?
  • 递归的checkForUpdate() 应该被链接到原来的promise 上还是只是开始一个新的独立promise 链?
  • 浏览器对同一主机同时运行的 ajax 调用数量是有限制的,因此后续调用将排队等待较早的调用完成。浏览器会在同一主机上同时运行多少个 ajax 调用而有所不同,但这是一个相对较小的数字(比如 10 个以下)。因此,如果您的 .each() 循环开始对同一主机进行一百次 ajax 调用,您将达到限制。
  • 而且,如果这是您真正的问题,为什么不将其放入问题中,以便我们直接解决?如果您更清楚自己观察到的内容和期望的内容,我们可以为您提供更好的帮助。一个小时后,我们在这里来回奔波,直到现在你才描述你实际观察到的和你期望的。当您隐瞒这些信息时,真的很难提供帮助。

标签: javascript promise


【解决方案1】:

您的代码同步运行$.each()。这意味着它将调用每个checkForUpdate(),然后才能运行其他任何东西。由于符合标准的 Promise 总是异步解决(在未来的某个时间),这意味着这里的每个请求都将在任何 Promise 可以运行其.then() 处理程序之前开始。这就是 promise 的工作方式。只有在 $.each() 循环完成后,Javascript 解释器才能开始处理已解析承诺的 .then() 处理程序。

此外,目前尚不清楚您为什么要在 setTimeout() 中创建 return checkForUpdate(environment, application, check)return 什么都不做。它只是返回到什么都不做的setTimeout() 回调。父函数早已返回,因此这不会将下一个 checkForUpdate() 链接到前一个承诺链。如果您想将它们链接在一起,那么您需要使用承诺进行延迟并返回该承诺,如这些参考中所示:

using setTimeout on promise chain

Delays between promises in promise chain

Delay chained promise


出乎意料的是,即使所有 100 个请求都在页面加载后立即发送,它们一次只被解决几个,并且(大致)按照它们发送的顺序。大致意思是 3, 2, 5, 1, 8, .... 但我希望像 3, 89, 12, 76, 21, 94, .... 有多少个 Promise 似乎有一些限制以什么顺序同时运行。

影响您的 ajax 调用的另一件事是,每个浏览器对允许对同一主机的并发 ajax 调用的数量都有限制。如果您超过该限制,它将排队它们并且在一些较早的完成之前不会运行后续的。每个浏览器都设置了自己的限制,并且随着时间的推移而变化,所以我不确切知道当前的限制是什么,但它们很低。我知道 Chrome 在同一台主机上曾经是 6 次。所以,这也会影响事情完成的确切顺序。

当您达到此限制时,将按照您的代码调用它们的顺序发送 Ajax 调用。因此,如果每个主机的限制是 6 个,那么您的前 6 个请求将被发送,而第 7 个请求只会在前 6 个请求中的一个完成时发送,依此类推。这仍然不能保证完成顺序,但它确实会影响后续请求在较早请求之前完成的能力。

【讨论】:

  • 添加了关于 setTimeout() 延迟的 cmets。
  • 请参阅问题编辑中的说明,如果有什么我可以澄清的,请告诉我。
  • @meridius - 我的回答不能解释你所看到的吗?你还在寻找什么我的答案没有描述的解释?听起来您想从$.each() 循环一次运行所有这些检查,这就是您的代码正在做的事情。在$.each() 循环完成启动所有操作之前,.then() 处理程序无法运行。这就是 Javascript 和 Promise 的工作方式。如果您希望它表现不同,请描述您希望它不做的事情。
  • @meridius - 添加了有关同一主机的浏览器 ajax 并发连接限制的信息。
猜你喜欢
  • 2020-10-31
  • 2018-08-27
  • 2016-11-29
  • 2019-06-30
  • 1970-01-01
  • 1970-01-01
  • 2013-09-16
  • 2016-10-16
  • 2019-04-29
相关资源
最近更新 更多