卸载事件能否可靠地用于触发ajax请求?

36

我需要一种方法来监控用户编辑会话,我正在审查的解决方案之一将使用unload事件发送ajax请求通知服务器编辑会话已结束。(参见:Monitoring User Sessions to Prevent Editing Conflict

我(相当有限的)阅读关于unload事件的资料表明,附加到此处理程序的代码必须快速运行,并且通常用于清除对象以防止内存泄漏。

我的问题是,这个方法能够可靠地达到这个目的吗?

PS. 我知道async: false选项。

5个回答

36

如果你的服务器响应速度足够快,这种方法是相当可靠的。但需要注意的是,如果在卸载事件上关闭浏览器并发送 AJAX 请求,很有可能在窗口对象被销毁之前无法及时从服务器获得响应。在这种情况下(至少在IE中),会使连接对象变成孤儿并且不会正确终止连接,直到连接超时时间到达。如果你的服务器没有打开持久连接,当你关闭两个窗口(同时还保留另一个窗口打开)后,你将没有足够的连接打开向该服务器发出请求(对于IE6-7而言,对于IE8而言则是6个窗口),直到连接超时时间到达你才能再次打开网站。

我曾遇到过这样的情况,我正在打开一个弹出窗口,在unload事件上发送了一个AJAX请求,它非常可靠,但是它被上述问题所困扰,并且我花费了很长时间来追踪并理解发生了什么。之后,我确保打开窗口具有与调用服务器相同的代码,并在每次unload时检查opener是否存在,如果存在则在此运行代码。

似乎如果你关闭最后一个浏览器窗口,IE会正确地销毁连接,但如果有其他窗口打开,则不会。

另外,值得一提的是,上面的答案中提到的,AJAX不是真正的异步。至少JS实现不是。在发送请求后,你的JS代码仍将等待服务器响应。它不会阻止你的代码执行,但由于服务器可能需要一段时间才能响应(或者足够长以使Windows终止IE窗口对象),你很可能会遇到上述问题。


1
有人尝试过关闭异步功能吗?我猜这样做会防止浏览器在请求返回之前卸载。 - Angry Dan
3
没错。关闭异步后,浏览器将在卸载页面之前等待服务器的响应。这有助于解决Chrome的缺陷,即在处理卸载事件和异步操作时非常不可靠。这也将修复我上面描述过的IE连接繁忙问题。但用户会注意到这一变化。 - Ilya Volodin
2
值得关注的是新的Beacon API,其中包括Navigator.sendBeacon,它将允许更可靠地在卸载时向服务器发出异步请求的更好方式:https://developer.mozilla.org/en-US/docs/Web/API/Navigator/sendBeacon - Wildhoney
同步请求已被弃用,使用async:false调用ajax将来会停止工作。请访问https://developer.mozilla.org/en-US/docs/Web/API/XMLHttpRequest并搜索“async”。 - Ruan Mendes
从Chrome 82版本开始,同步请求支持已被移除。 - Jagath01234

12

你尝试过使用吗?

var i = new Image(1,1); 
i.src='http://...'

只需从服务器返回一些空图像。 我认为它应该是可靠的,脚本将被阻止。 顺便说一句:很好地添加时间戳以防止缓存。


我发现在onload事件函数中使用AJAX调用不起作用,但是这种方法可以。需要注意的是,该函数代码不会停止或阻止页面卸载,代码可能会在页面卸载后执行。 - nuander

3
我们有一个需要这样做的案例。这是一个报告页面,需要服务器上的大量内存,因此我们希望在用户离开页面时立即释放它。我们创建了一个 frameset 并添加了 unload 处理程序。最可靠的方法是将图像的 src 设置为释放脚本。我们实际上使用了 unload 和 onbeforeunload 来实现跨浏览器兼容性。它在 web kit nightlies 中不起作用,但管理层对此表示满意。
然而,那并不是我的建议解决方案。我会使用心跳方法,这需要更多的工作,但更加稳健。
您的页面应该发送定期的心跳请求。每个请求都会设置页面的最后心跳时间。然后,您需要在服务器上运行一个线程,如果最后的心跳时间太久远,则清除内存。
这并不能解决长时间保持页面打开的问题。为此,您需要一些用户活动监控,在一段不活动时间后自动离开该页面(请确保与用户确认)。

你真的需要 frameset、图像和释放脚本方法吗?这似乎有些hackish...或者相比使用两个 unload 事件,这更可靠吗? - Yi Jiang
我们需要框架集,因为页面内有导航,我们只想在离开页面集时发送“释放内存”请求。图像/双卸载方法是我们让它在各种浏览器和操作系统上最可靠运行的方式。 - Ruan Mendes

1

你需要自己测试一下,看看你的特定情况是否能在“unload”中使用,但是由于AJAX是异步的,发送AJAX请求非常快。你只需发送请求,然后就完成了!(也许你还需要清除刚刚创建的请求对象)

如果你想验证AJAX请求是否成功,那么你就需要更担心,使用async:false选项(如this discussion所示)。但是,只发送请求就是一个快速的操作。


1
如果它无法连接服务器,我不会称其为可靠的,对吧? ;) - Yi Jiang
1
没错,但我以为你的意思是浏览器会真正发送请求,这样才算可靠。 - palswim

-2

我曾经遇到这样一个情况,我只需要通知服务器端卸载并不关心响应。

如果你也是这种情况,你可以使用ignore_user_abort,这样你就知道它会“可靠地”发生。


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