javascript 文件挂起时 SignalR 连接块
Posted
技术标签:
【中文标题】javascript 文件挂起时 SignalR 连接块【英文标题】:SignalR connect blocks while javascript file is pending 【发布时间】:2021-10-12 16:23:27 【问题描述】:我在包含标准 google 标签管理器脚本的页面上使用 SignalR。该脚本是异步加载的 (defer=true)。
SignalR 用于可以在该页面上触发的一些长时间运行的任务,因此我禁用触发这些操作的按钮,直到 SignalR 准备好,即当对 connect() 的调用成功返回时。
一些客户报告说我需要很长时间才能激活按钮。这些客户位于阻止 google tag manager 的公司网络上,由于某些原因,对 googletagmanager.com 的请求并不总是立即返回状态 403。有时它只在超时后(如 2 分钟后)返回 403。
我不在乎客户是否阻止谷歌标签管理器,但我的 SignalR 连接调用(在文档准备就绪时)将停止,直到对谷歌标签管理器的调用完成。它不应该停止,因为它们在逻辑上应该是独立的,并且脚本是用 defer=true 加载的。
我不知道为什么连接调用不会更快完成。我发现的唯一一件事是“不要使用 SignalR 的会话状态”(例如SignalR doesn't use Session on server)。我在我的 Web 应用程序中使用 Session 对象,但不是来自 SignalR 集线器。
使用 .NET Framework 4.5 和 SignalR 2.4.1
SignalR 在服务器上的启动是标准的:
using Microsoft.Owin;
using Owin;
using MyNamespace;
[assembly: OwinStartup(typeof(SignalRStartup))]
namespace MyNamespace
public class SignalRStartup
public void Configuration(IAppBuilder app)
// Any connection or hub wire up and configuration should go here
app.MapSignalR();
Hub 实现对 Session 没有任何作用
public class CollectiefHub : Hub
private static readonly IHubContext sHubContext = GlobalHost.ConnectionManager.GetHubContext<CollectiefHub>();
public const string MsgCalcReady = "CalcReady";
public const string MsgDocGenReady = "DocGenReady";
public Task JoinGroup(string groupName)
return sHubContext.Groups.Add(Context.ConnectionId, groupName);
public Task LeaveGroup(string groupName)
return sHubContext.Groups.Remove(Context.ConnectionId, groupName);
private static async Task Send(string group, object msg)
await sHubContext.Clients.Group(group).receive(msg);
public static async void SendMessage(string groupName, object message)
await Send(groupName, message);
public static async void HandleListDone(string groupName, Dictionary<string, string> list)
await Send(groupName, new list );
public static async void HandleListProgress(string groupName, string guid, int percentage)
await Send(groupName, new guid, percentage );
javascript 代码:
var AsyncCalc =
connection: $.hubConnection(),
collectiefHub: null,
group: window.location.pathname.split("/")[2], // Contains identifier
calcDoneEvent: "asyncCalcDone",
asyncObject: null,
connected: false,
setupSignalR: function()
AsyncCalc.collectiefHub = AsyncCalc.connection.createHubProxy("collectiefHub");
AsyncCalc.collectiefHub.on("receive", function (message)
// Handle specific messages here
);
// Start connection
AsyncCalc._start();
,
_start: function ()
AsyncCalc.connection.url = "/" + window.location.pathname.split("/")[1] + "/signalr";
AsyncCalc.connection.start().done(function ()
AsyncCalc.connected = true;
AsyncCalc.collectiefHub.invoke("joinGroup", AsyncCalc.group);
$(".async").removeClass("disabled");
);
;
$(function ()
AsyncCalc.setupSignalR();
);
Google 标签管理器只是一个 javascript js 文件,它包含在我页面的 <head>
部分中,如下所示:
<script async src="https://www.googletagmanager.com/gtm.js?id=GTM-XXXXXX"></script>
有什么想法吗?
【问题讨论】:
你能展示一些你的代码吗?这可能是由多种原因造成的。让我们看看您的 javascript http 请求和信号器启动代码。您是直接从 javascript(谷歌请求)发出这些请求,还是先发送到您的服务器?他们共享同一个 HttpClient 吗? @fix 添加了相关代码。谷歌标签管理器脚本由客户端加载,因为它只是包含在页面的头部部分中。服务器只参与与客户端建立 SignalR 连接。这就是为什么我对 SignalR 连接在客户端对 googletagmanager.com 的调用完成之前还没有准备好这一事实感到困惑的原因。谷歌标签管理器本身似乎无关紧要,这只是一个被某些公司防火墙阻止的外部脚本。可能是另一个外部脚本,恰好是这个脚本触发了问题。 【参考方案1】:即使您在被阻止的脚本上使用defer
attribute,脚本仍必须在 DOMContentLoaded 事件触发之前尝试运行,而这正是 jQuery 的就绪函数等待的事件。我不确定这是否同样适用于 async
属性。您希望在需要启用的按钮出现在页面上时立即调用AsyncCalc.setupSignalR();
,而不是等待 DOMContentLoaded 事件。根据按钮的加载方式,将脚本标签移动到 html 正文的底部可能就足够了。如果没有,您可以使用MutationObserver 或类似的东西来等待按钮。
如果按钮异步加载,您仍然可以立即调用AsyncCalc.setupSignalR();
函数,只等待按钮出现运行$(".async").removeClass("disabled");
。
【讨论】:
我使用 Fiddler 进行了更多调试,并向控制台添加了一些日志记录。DOMContentLoaded
事件触发并调用 AsyncCalc.setupSignalR()
。 AsyncCalc.connection.start()
(在 setupSignalR 中被调用),但它在那里停止。在加载每个远程脚本之前没有协商调用。
虽然典型的 signalR 调用 (negotiate/connect/start) 在加载 gtm 脚本之前不会出现在网络流量中,但从客户端到服务器的其他 ajax 调用执行得很好。服务器正在接受连接,但不知何故 AsyncCalc.connection.start()
不会立即调用服务器。
您使用哪个 jQuery 插件来提供$.hubConnection()
?我能找到的唯一一个已弃用。官方MS客户端不依赖jQuery:npmjs.com/package/@microsoft/signalr以上是关于javascript 文件挂起时 SignalR 连接块的主要内容,如果未能解决你的问题,请参考以下文章
进度条不适用于 zipfile?当程序似乎挂起时如何提供反馈
挂起时GmsClient“在仍然连接时调用connect()”
原生 Android 应用程序在挂起时是不是应该释放 OpenGL 资源?