JavaScript:等待Ajax请求完成后关闭页面

12

我希望浏览器在发送ajax请求之前保持页面的打开状态。我想象中的效果是这样的:

var requestsPending = 0;

window.onbeforeunload = function() {
    showPleaseWaitMessage();
    while(requestsPending > 0);
}

// called before making ajax request, atomic somehow
function ajaxStarted() {
    requestsPending++;
}
// called when ajax finishes, also atomic
function ajaxFinished() {
    requestsPending--;
}

很遗憾,JS不支持多线程。据我理解,回调函数(ajaxFinished)可能永远不会被执行,因为浏览器会尝试等待while循环结束才执行它,从而导致无限循环。

该怎么做才是正确的呢?也许有一种方法可以强制JS评估其待办事项清单中的下一步,然后再返回while循环?或者某种"加入"ajax调用的语法?我正在使用DWR进行我的ajax。

谢谢, -Max

2个回答

13

编辑 根据您下面的评论,修订后的答案:

如果你想要阻塞直到一个 之前已经启动 的请求完成,可以这样做:

window.onbeforeunload = function(event) {
    var s;
    event = event || window.event;
    if (requestsPending > 0) {
        s = "Your most recent changes are still being saved. " +
            "If you close the window now, they may not be saved.";
        event.returnValue = s;
        return s;
    }
}
浏览器会提示用户是否离开该页面,让他们有控制权。如果他们留在页面上而请求已完成,则下次关闭页面时,它将允许他们关闭而无需询问。
请注意,在现代浏览器上,您的消息不会显示;相反,浏览器将使用通用消息。因此,在现代浏览器上,返回任何非空字符串都足够了。但是,您可能希望返回一个有用的字符串(如上所示),以防您的用户使用仍会显示消息的过时浏览器。
更多关于询问用户是否取消关闭事件的信息可以在这里这里这里

旧回答

理想情况下,如果可能的话,你希望避免这样做。:-)

如果无法避免,可以使Ajax请求同步,以便它阻止onbeforeunload进程直到完成。我不知道DWR,但我希望它有一个标志来控制请求是否同步。在原始XmlHTTPRequest API中,这是open的第三个参数:

req.open('GET', 'http://www.mozilla.org/', false);
                                            ^ false = synchronous

大多数JavaScript库都有相应的解决方法。例如,在Prototype中,它是选项中的asynchronous: false标志。

但是,如果可能尽量避免在页面卸载时触发Ajax请求。请求设置、传输和完成期间将会有明显的延迟。最好让服务器使用超时来关闭您试图关闭的任何内容。(它可以是一个相当短的超时时间;可以在页面开放时定期使用异步Ajax请求来保持会话活动状态,比如每分钟发送一次,并在两分钟后超时。)


1
即使你将它设置为同步,浏览器也不会等待,至少不是所有的浏览器……所以它仍然不可靠(而且在我看来应该是这样的,在我的浏览器中,因为10个标签页中的其中一个想要发送一些我不关心的 AJAX 请求而导致浏览器挂起)。 - Nick Craver
1
@Nick: 你是在说 beforeunload 还是 unload?因为在 beforeunload 中,如果我愿意,我可以启动像 alerts 这样具有侵入性的东西。虽然我已经很久没有在 beforeunload 中进行这种请求发送了(因为我认为这不是一个好主意),但是当我几年前这样做时,它在 IE6、IE7 和 Firefox... 嗯...几年前的 Firefox 中可以可靠地工作。 :-) - T.J. Crowder
1
@T.J. 警报会阻塞UI线程,它不像ajax一样是回调,因此情况不同...像OP试图做的那样,在任何情况下都不起作用(大多数情况下),这是一个“点火并忘记”的过程...但即使是点火部分完成也取决于浏览器。 - Nick Craver
@Nick:我得再试一次(现在不能)。但根据OP的评论,结果是,这不是他想做的事情! :-) - T.J. Crowder
@maxdj - 为什么不在ajax请求返回之前禁用该链接? - Nick Craver
显示剩余4条评论

4
简而言之,您不能(也不应该)这样做。如果用户关闭浏览器,它就会关闭...没有保证完成的unload样式事件,而使用涉及延迟的AJAX的某些操作更不可能完成。
您应该考虑在其他时间点触发事件,或者完全改变方法,但在unload事件中进行AJAX调用将是不可靠的,最好情况下也是如此。
作为上述“不应该”部分的补充,从这个角度来考虑一下,您通常在任何给定窗口上打开多少个选项卡?我通常有4-6个带有5-12个选项卡的Chrome窗口...如果其中1个选项卡想要进行一些我不关心的AJAX请求,我的浏览器窗口应该挂起吗?作为一个用户,我不希望它这样做,所以作为开发人员,我也不会尝试这样做。当然,这只是一个观点,但值得思考。

那么,这个问题有什么好的解决方案呢?例如,假设您有一个可重新排序的元素列表,并附有编辑链接。如果用户重新排序列表,然后在ajax告诉服务器列表已被重新排序之前单击链接,则会失去更改,因为浏览器转到编辑页面。 - maxdj
@maxdj - 一个.live()处理程序的例子,如果有ajax请求正在进行,则返回false,如果没有,则继续...任何防止链接在不应该工作时起作用的东西。 - Nick Craver

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接