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 文件,它包含在我页面的 &lt;head&gt; 部分中,如下所示:

<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 资源?

当应用程序挂起时,我如何知道 NSURLSessionUploadTask 是不是正在工作?

应用退出或挂起时处理推送负载

挂起时,Supervisorctl 不会自动重启守护进程队列工作者