如何在单击按钮后异步多次更新标签

问题描述 投票:0回答:1

我有一个基于 .Net Framework 4.8 构建的网站。我有一个按钮,当单击它时,它将按顺序同步对服务器进行远程调用。可能有数千个操作,每个操作可能需要很多秒才能响应。

因此,我希望页面上有一个标签指示当前进度,例如“执行的 1/1000 操作”和“当前操作:启动”。我怎样才能实现这个目标?

这是模拟该场景的示例代码:

<asp:Button ID="StartButton_Click" OnClick="StartButton_Click" Text="Start" runat="server"/>
<asp:UpdatePanel ID="ProgressUpdatePanel" runat="server">
    <ContentTemplate>
        <asp:Label ID="ProgressLabel" runat="server"></asp:Label>
    </ContentTemplate>
    <Triggers>
    </Triggers>
</asp:UpdatePanel>

以及模拟远程调用的处理程序:

protected void StartButton_Click(object sender, EventArgs e)
{
    List<string> actions = (List<string>)Enumerable.Repeat("0", 100);
    processItem(actions, 0);
}

private void processItem(List<string> items, int index)
{
    if(index >= items.Count)
    {
        return;
    }
    else
    {
        System.Threading.Thread.Sleep(3000);
        /* Can't update ProgressLabel.Text here,
           because I can't update anything before the page is returned to the client*/
        processItem(items, index + 1);
    }
}

我得到的最接近的是使用 TimerControl 执行 asyncPostBacks 来读取进度并更新标签,但这感觉像是一种解决方法,我想知道是否有更好的方法。

<asp:Button ID="StartButton_Click" OnClick="StartButton_Click" Text="Start" runat="server"/>
<asp:Timer ID="Timer1" OnTick="Timer1_Tick" runat="server" Interval="1000" Enabled="false" />
<asp:UpdatePanel ID="ProgressUpdatePanel" runat="server">
    <ContentTemplate>
        <asp:Label ID="ProgressLabel" runat="server"></asp:Label>
    </ContentTemplate>
    <Triggers>
        <asp:AsyncPostBackTrigger ControlID="Timer1" />
    </Triggers>
</asp:UpdatePanel>
static currentCount = 0; //updated by processItem

protected void Timer1_Tick(object sender, EventArgs e)
{
    ProgressLabel.Text = $"{currentCount}/{totalCount} actions performed";
}

我也尝试过在

ViewState
中强制回发并保存状态,但这需要在客户端自动触发回发,感觉真的很不对。

c# asp.net asynchronous
1个回答
0
投票

首先,20 个,或者 100 个?那么好吧。但是,您的帖子建议在 1000+ 范围内。这是一个非常不同的挑战和计算机问题。

请记住,谷歌、微软等公司在面试中会提出上述类型的问题(诸如多大、多远、多长的问题)。出现此类问题的原因是,网页上的某些进度系统(例如 20 个甚至 50 个项目)之间存在巨大差异。但是,1000+ 是一个全新的不同类型的问题和挑战。

网站不太可能需要运行和控制 1000 多个要做的事情。网站可能会“监视”此类过程的进展情况,但不负责运行此类代码......

这不再是网站类型的应用程序。

我们必须区分“实时”更新和监控,而不是说调用一些 Web 服务器代码 + 运行 1000 个不同的进程。

这很像我们每 5 秒获取并显示一些机器设备的温度读数,甚至是用于显示的外部温度计值的差异。

当我们开始谈论 1000+ 个进程时,我们就无法知道、不知道、也不会知道每个进程运行所需的时间。而且网站不够可靠,无法负责调用这么多例程。 然而,针对此类处理的某种轮询是有意义的。

那么,比如说 40 次或 75 次操作?那么当然,我们可以基于网络进行此操作。

但是,在我们发布一些适用于简单用例的简单代码之前?

如果您正在寻找实时更新?

那么您最好采用 SignalR,因为它是专为此类任务而设计的。

SignalR 证明服务器与浏览器的实时 Web 套接字连接。因此,对于“与朋友聊天”类型的应用程序来说,两者都可以在网络浏览器中输入内容,并且都可以实时看到彼此正在输入的文本。

因此,SignalR 技术也适用于工厂车间的监控设备,甚至是外部温度的简单显示。因此,对于这种类型的实时更新(对于超出正常情况的疯狂情况,您有 1000 多个进程需要监控)?

您可以从这里开始使用 SignalR (它是为这些实时更新和过程监控类型的问题而设计的)。 SignalR 将允许您的服务器端代码甚至在循环中将状态更新到浏览器中)。

因此,如果您需要跟踪 1000 多个处理事件,那么 SignalR 就是您的最佳选择:

链接在这里:

https://learn.microsoft.com/en-us/aspnet/signalr/overview/getting-started/

但是,对于我们这些凡人来说,还有一个简单的处理和更新循环吗?

说出这个标记:

        <asp:UpdatePanel ID="UpdatePanel1" runat="server">
            <ContentTemplate>

             <asp:Button ID="cmdProcess" runat="server" Text="to hide button" ClientIDMode="Static"
                  OnClick="cmdProcess_Click" style="display:none"  />

                <asp:Button ID="Button1" runat="server" Text="Start the Reactor"                        
                    ClientIDMode="Static" CssClass="btn"
                    OnClientClick="startprocessor('Processing',1,18);return false"
                    />
                <br />
                    <asp:HiddenField ID="pStep" runat="server" ClientIDMode="Static" />
                    <asp:HiddenField ID="pSteps" runat="server" ClientIDMode="Static" />

                <div id ="MessageArea" style="display:none">

                    <style>
                        .ui-progressbar-value {background: lightskyblue;}
                    </style>

                    <div id="pbar" 
                        style="width:400px">
                    </div>

                    <asp:Label ID="lblmsg" runat="server" Text="" ClientIDMode="Static"></asp:Label>
                    <br />
                </div>
            </ContentTemplate>
        </asp:UpdatePanel>
               
        <script>

            var prm = Sys.WebForms.PageRequestManager.getInstance();
            prm.add_endRequest(EndRequestH);

            function EndRequestH(sender, args) {
                // code here
                lbl = $('#lblmsg')
                if (lbl.text() !== "") {
                    // more processing required
                    $('#MessageArea').show()
                    iStep = $('#pStep').val()
                    iSteps = $('#pSteps').val()
                    lbl.text("(" + iStep + "/" + iSteps + ") " + lbl.text())
                    $('#pbar').progressbar({ value: iStep / iSteps * 100 });
                    $('#cmdProcess').click()
                }
            }

            function startprocessor(sMsg, iStep, iSteps) {
                var mymsg = "(1/" + iSteps + ") " + sMsg
                $('#lblmsg').text(sMsg)
                $('#pStep').val(iStep)
                $('#pSteps').val(iSteps)
                EndRequestH()
            }
        </script>

因此,我们有一个更新面板,设置处理步骤,然后单击按钮。后面的代码运行,完成后,客户端代码检查下一步,如果是,则再次单击按钮。

背后代码:

    protected void Page_Load(object sender, EventArgs e)
    {
    }
    
    protected void cmdProcess_Click(object sender, EventArgs e)
    {
        int Step = Convert.ToInt32(pStep.Value);
        int Steps = Convert.ToInt32(pSteps.Value);

        switch (Step)
        {
            case 1:
                // code here for step one
                Thread.Sleep(2000);
                // if more steps, then setup next message
                pStep.Value = "2";
                lblmsg.Text = "Sending emails...";
                return;

            case 2:
                // code here for step 2
                Thread.Sleep(3000);
                pStep.Value = "3";
                lblmsg.Text = "Building Projects for customers...";
                return;

            case 3:
                // code here for step 3 - Building project for custoemrs
                Thread.Sleep(2000);
                pStep.Value = "4";
                lblmsg.Text = "Taking pet dog for a walk..";
                return;

            case int n when (n > 3) && (n < Steps):
                Thread.Sleep(1000);
                pStep.Value = (Step + 1).ToString();
                lblmsg.Text = "Working, please wait";
                return;

            case int n when (n == Steps):

                // process code here for last step
                Thread.Sleep(1000);


                // we done last step, so stop
                // last step - clear out lbl msg to STOP processing
                lblmsg.Text = ""; // blank message - stop processor
                return;
        }
    }

结果如下所示:

因此,上述方法适用于大约 20 或 75 个流程步骤。如果您需要更多步骤,那么您需要采用不同的技术,例如 SignalR。

© www.soinside.com 2019 - 2024. All rights reserved.