如何取消 AJAX 长时间运行的 MVC 动作客户端(在 javascript 中)?

Posted

技术标签:

【中文标题】如何取消 AJAX 长时间运行的 MVC 动作客户端(在 javascript 中)?【英文标题】:How do you cancel an AJAX long running MVC action client side (in javascript)? 【发布时间】:2012-02-13 10:38:41 【问题描述】:

我有一个长时间运行(4-10 秒)的 MVC 操作,它运行来自 AJAX 调用的报告。当它运行时,用户可以更改参数并运行其他东西,所以我在发出另一个 AJAX 请求之前取消了它。

因此,例如(例如在 jQuery 中,但无论如何都会发生问题):

// If we have an active request and it's not complete
if(dataRequest && dataRequest.readyState != 'complete') 

    dataRequest.abort();


dataRequest = $.ajax(...);

客户端这似乎工作正常,但取消的请求仍在服务器上运行。例如,如果报告需要 10 秒,而我取消一个并启动另一个,那么第二个请求需要 20 秒。

我认为这是由于session level locking:

[如果] 对同一个会话发出两个并发请求(通过使用 相同的 SessionID 值),第一个请求获得独占访问 会话信息。第二个请求仅在 第一个请求已完成。

因此,在第一个请求完成之前,第二个请求无法访问会话。使用异步 MVC 操作似乎无法解决此问题,因为操作仍然需要能够对会话进行更改。

是否可以在不使用AsyncController[SessionState(SessionStateBehavior.ReadOnly)] 的情况下停止一项操作并开始下一项操作?

如果不是两者都需要?

【问题讨论】:

【参考方案1】:

[SessionState(SessionStateBehavior.ReadOnly)] 或完全禁用会话足以允许从同一会话并发访问控制器操作。所以第二个请求(来自同一个会话)不需要等待第一个请求完成后才被处理。就取消服务器上的第一个操作而言,这将更难。您必须将每个任务关联一个唯一的 ID,并在启动新任务时将此任务 ID 返回给客户端。然后,当您通过调用 .abort() 取消客户端上的 AJAX 请求时,您可以向其他控制器操作触发另一个 AJAX 请求并将唯一的任务 ID 传递给它。这个控制器动作本身会设置一些通用标志来指示要停止的第一个动作。

TPL 具有内置的取消支持,您可以考虑简化它,以避免两者之间的共享数据结构将同步它们。

【讨论】:

我已经在使用 TPL 来完成很多需要时间的工作 - 我正在以大约 4-10 秒的时间并行执行大约 25 秒的长时间运行的连续代码。然而,用它来处理中止似乎是在重新发明***。为什么新请求需要告诉旧的(仍在运行的)请求在它已经被取消时中止? @Keith,不是新请求会告诉旧请求中止。您必须发送一个新的特殊 AJAX 请求来告诉服务器中止特定任务的处理。当您执行dataRequest.abort(); 时,您只是中止了 AJAX 请求,但如果您想中止服务器端处理,您需要做的工作远不止于此。 嗯,所以 IIS 不知道请求已在传输级别取消?当等待连接在传输级别终止时,我希望页面线程在 IIS/ASP 级别中止。 IIS 确实如此——我需要做的就是在长时间运行的页面中检查Response.IsClientConnected。当该属性变为假时,我会取消我正在做的事情。【参考方案2】:

事实证明,完整的答案需要两件事:

首先(感谢 Darin 的confirming it)会话基本上锁定了要一个接一个地执行的页面。 Further investigation 指出这是 ASP.Net 会话的一个更普遍的问题——它们无法处理乐观并发。

其次,取消的请求需要检查客户端是否仍然连接。在某些情况下,您可能想要继续(例如火灾并忘记 ASP 操作),但在这种情况下,如果客户不再等待报告,我们就没有必要继续处理它了。

这可以作为property of the response:this.Response.IsClientConnected使用

因此,为了在 jQuery 请求时有效地取消我的服务器端操作,我必须编写自己的会话处理程序并添加针对 this.Response.IsClientConnected 的定期检查。

【讨论】:

小心依赖IsClientConnected,因为检查它显然是一个严重的性能问题,请参阅***.com/questions/9094735/… 嗨,基思!我有类似的问题,但即使你的回答也无法解决,(***.com/questions/29235850/…) 有什么办法可以帮助我吗?或者您可以发布您的示例代码来触发this.Response.IsClientConnected【参考方案3】:

.abort() 只是客户端取消。如果你想在服务器端监控它,一个好的方法是轮询 IsClientConnected 属性,它会告诉你 ajax 通信的状态是什么。

【讨论】:

查看接受的答案和原始问题。这个答案没有添加任何尚未说明的内容。使用IsClientConnected 时也要非常小心,因为检查它显然是一个严重的性能问题,请参阅***.com/questions/9094735/…(显然检查它可能花费高达½s,但没有人能够确认) 你说得对,我已经阅读了你的帖子,我必须修改我的解决方案。

以上是关于如何取消 AJAX 长时间运行的 MVC 动作客户端(在 javascript 中)?的主要内容,如果未能解决你的问题,请参考以下文章

需要一个带有用户反馈的 ASP.NET MVC 长时间运行的进程

如何异步运行长时间运行的操作并在 ASP.Net Ajax 中为用户显示加载并轮询结果?

为啥ubuntu长时间无动作黑屏后无法唤醒

向客户端发送有关视图中长时间运行任务的进度的更新

CancellationToken 永远不会取消我长时间运行的加载数据功能

Elastic Search 取消长时间运行的任务