如何在用户离开页面时取消所有逻辑的执行
本文关键字:执行 取消 用户 离开 | 更新日期: 2023-09-27 18:04:03
不确定这是否可能,但是当用户离开页面转到另一个页面时,我希望当前页面上的所有服务器逻辑都结束执行。我不希望用户看到一个错误,来自一个页面,他们以前上(例如长时间运行的SQL查询超时)。我该怎么做呢?
执行长时间运行的查询需要多长时间?我可能会专注于优化这部分和应用程序的整体设计。
如果你正在加载数据网格中的数据,例如,你调用业务逻辑调用数据层从数据库中加载数据块,如果查询是快速的,你的服务器端执行查询,同时用户关闭浏览器或导航离开,结果根本不会被消耗,因为没有页面加载和渲染数据了。
如果你真的有很长时间的服务器端操作,你想控制和终止依赖于应用程序的导航,我认为这些操作不应该同步执行,我将异步监控这些,并简单地显示结果在一个页面,每当用户打开这样的页面。
您可以使用Response.IsClientConnected
来了解您的客户端是否仍在阅读您的页面。
if (!Response.IsClientConnected)
{
// Stop
}
我同意你需要集中精力来避免长时间的循环使你的用户走了,但是如果你想看看你的用户是否还在你的页面上,你可以使用上面的命令。您可以不时调用它来查看。
我的网站http://www.iprotein.info也有同样的问题。基本上,它运行在来自SQL数据库的大型数据集(300k)上,并进行复杂的计算,如果查询很大,可能会花费一些时间。当我把这个网站上线时,我发现有些用户发出了任务,但是决定不等待计算就离开了页面,而服务器一直在计算,直到所有的任务都完成。这会导致严重的CPU拥塞,浪费CPU时间在放弃的任务上。
所以我的解决方案是:
控制器为每个新页面分配一个唯一的ID。
private static ConcurrentDictionary<int, CancellationTokenSource> tasks = new ConcurrentDictionary<int, CancellationTokenSource>();
private static TaskID taskID = new TaskID();
const int taskCap = 10000;
public IActionResult SearchPage()
{
lock (taskID)
{
if (taskID.id > taskCap)
taskID.id = 0;
ViewBag.taskID = taskID.id;
taskID.id++;
}
return View();
}
客户端通过ID:
告诉服务器哪个任务将被终止$(window).unload(function () {
var xmlHttp = new XMLHttpRequest();
xmlHttp.open("GET", '@Url.Action("CancelTask")', true);
xmlHttp.send(@ViewBag.taskID);
});
模型:
public class TaskID
{
public int id;
public TaskID()
{
id = 0;
}
}
在搜索调用中,分配一个CancellationTokenSource以允许取消任务,并将其存储在具有线程ID的静态映射中。_SearchAction()才是真正要做的事情。
[HttpPost]
public JsonResult SearchAction(string query, int instanceID)
{
lock (tasks)
{
tasks[instanceID] = new CancellationTokenSource();
}
CancellationToken ct = tasks[instanceID].Token;
var searchTask = Task.Factory.StartNew(() => _SearchAction(query, instanceID, ct));
JsonResult res;
try
{
res = searchTask.Result;
}
catch (OperationCanceledException)
{
res = null;
}
lock (tasks)
{
CancellationTokenSource temp;
tasks.Remove(instanceID, out temp);
temp.Dispose();
}
return res;
}
当接收到CancelTask调用时,取消Task:
public void CancelTask(int id)
{
if (interactionCache.ContainsKey(id))
{
List<Tuple<int, int>> list;
interactionCache.Remove(id, out list);
}
if (tasks.ContainsKey(id) && tasks[id] != null)
{
tasks[id].Cancel();
}
}