如何在用户离开页面时取消所有逻辑的执行

本文关键字:执行 取消 用户 离开 | 更新日期: 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();
            }
        }