Search code examples
javascriptc#asp.net

JS function delay on client side C#


I have web app in asp.net which measure database query speed. I created gauge in my html code and have JS function which refresh this gauge like this:

    window.onload = function () {

        gauge1 = new JustGage({
            id: "gauge1",
            value: 0,
            min: 0,
            max: 10,
            showInnerShadow: true,
            shadowOpacity: 2,
            shadowSize: 5,
        });

    function updateGauge1Value(newValue) {
        gauge1.refresh(newValue);
    }

The aim is that when I click on start button on my webpage, there should start query execute in DS and I want to refresh my gauge after every one query is finished, so it means 100 times in function according to seconds measures on my timer. But the gauge refresh is done just after whole function with finish elapsed time but not when the update function is called via ClientScript, so I want something like this:


        protected void Button_Click(object sender, EventArgs e)
        {
            //I connect to Database

            for(int i = 0; i < queries.Length; i++){
            //Start timer
            query.Execute();
            string jscode =
            $@"updateGauge1Value(timer.ExecuteSeconds())";

            jscode = "setTimeout(function () {" + jscode + "},1)";
            ClientScript.RegisterStartupScript(Page.GetType(), "myfunc", jscode, true);
            }
         }

It is somehow possible to call my JS function during button click void is executing?

I tried put it into different functions, but doesn't help.


Solution

  • As noted, you need that grasp of the page life cycle (without that grasp, then this becomes confusing).

    However, since we going to click a button to start something, we probably don't want to post the page back to the server. Since while that page is on the server, we can update text box 1 to 10, do 2 or 10 register startup scripts, and ALL that code is doing is modifying the copy of the page on the server. While on the client side, we still waiting for that WHOLE page to come back!

    In other words, be it script adding (register scripts), update of a control? All that modifying of the web page occurs 100% server side. That includes the "inject" or "adding" of our scripts. We can register 5 scripts, but all we doing is modifying the page up on the server. Those scripts will NOT run until the WHOLE web page travels back to the client side.

    A simple change of a text box, or register script? User will not see ANY OF the code behind results until such time the whole page travels back to client side, THEN it renders, and THEN the JavaScript starts running.

    So, there are several approaches here. A "fancy-pants" solution could be to adopt SignlR. However, that's quite a bit of code for just some timed test of a query. (but, you can consider SignalR - SignalR is a code library that allows you to "push" data from the server. You can use that system for all kinds of things - say like even building a chat system to allow two people to type and see the results on one web page).

    However, since the (we will assume) the page is loaded, gauges rendered, and then we want a start button to do some query work server side.

    There are as noted, a boatload of ways to approach this, but I would let the page load, render, and then have your start button NOT do a post back, but in fact:

    Start a client side timer - we could have a cool looking update of our one "time elapsed" gauge while we wait for the query to run. When done, we could stop our gauge.

    So, since we don't want a post-back (at least I don't think we should deal with one), then I suggest a simple web method).

    So, let's drop in 3 gauges. When we click the button, will will NOT yet run code behind, and thus NOT post-back the page.

    We will start a timer (and display update) of our first gauge.

    Then we wait for server to do the query work, and return some values we want. In this "made up" example, the query will grab the number of PDF files, and Zip files loaded (the date time and upload is a in a database).

    So, we will return Total Files, Zip files, Pdf files uploaded (this data of uploaded logged files is in a database table).

    Now, since the query runs OH SO very fast, I will insert a fake 4 second delay into that server side code.

    So, while "many" approaches exist here, let's go with a web method (ajax call).

    I assume we have jQuery here.

    So, we have this markup: (nothing special)

        <div style="padding:35px">
    
            <div style="width:200px;float:left">
    
                <div id="gauge0" style="width:130px"></div>
                <div style="clear:both"></div>
                <div>
                    <asp:Button ID="cmdStart" runat="server" Text="Start Query"
                        style="margin-left:20px" CssClass="btn"
                        OnClientClick="mystart();return false"                        
                        />
                </div>
            </div>
            <div id="gauge1" style="float:left;width:200px;"></div>
    
            <div id="gauge2" style="width:200px;float:left; margin-left:40px; width: 200px"></div>
    
        </div>
    
        <script>
    
            var gauge0;
            var gauge1;
            var gauge2;
    
            var MyTimer;
            var ElapsedMSecs = 0.0
    
            function mystart() {
                ElapsedMSecs = 0
                gauge0.refresh(0,10,0)
                gauge1.refresh(0,50,0)
                gauge2.refresh(0,50,0)
                MyTimer = setInterval(mycounter, 100)
    
                $.ajax({
                    type: "POST",
                    url: "GuageTest2.aspx/GetStats",
                    data: {},
                    contentType: "application/json; charset=utf-8",
                    dataType: "json",
                    success: function (MyStats) {
                        clearInterval(MyTimer)
    
                        var TotalFiles = MyStats.d.TotalFiles
                        var PdfFiles = MyStats.d.PdfFiles
                        var ZipFiles = MyStats.d.ZipFiles
    
                        gauge1.refresh(PdfFiles, TotalFiles, 0)
                        gauge2.refresh(ZipFiles, TotalFiles, 0)
    
                    },
                    error: function (xhr, status, error) {
                        var errorMessage = xhr.status + ': ' + xhr.statusText
                        alert('Error - ' + errorMessage)
                    }
                });
    
                // gauge0.refresh(6,10,0)
            }
    
            function mycounter() {
    
                ElapsedMSecs += 0.1
                if (ElapsedMSecs >= 10) {
                    clearInterval(MyTimer)
                }
                else {
                    gauge0.refresh(ElapsedMSecs)
                }
    
            }
    
    
            window.onload = function () {
    
                gauge0 = new JustGage({
                    id: "gauge0",
                    value: 0,
                    min: 0,
                    max: 10,
                    label: "Query Time",
                    showInnerShadow: true,
                    shadowOpacity: 2,
                    shadowSize: 5,
                    donut: true,
                    relativeGaugeSize: true,
                    counter:false,
                    guageColor: "#0094ff",
                    decimals:2,
                    levelColors: [
                        "#00fff6",
                        "#ff00fc",
                        "#1200ff"
                    ]
                });
    
                gauge1 = new JustGage({
                    id: "gauge1",
                    value: 0,
                    min: 0,
                    max: 10,
                    label: "Pdf Files Uploaded",
                    showInnerShadow: true,
                    shadowOpacity: 2,
                    shadowSize: 5,
                    refreshAnimationType: "bounce",
                    startAnimationType: ">",
                    counter: true,
                    refreshAnimationTime: 2000
                });
    
                gauge2 = new JustGage({
                    id: "gauge2",
                    value: 0,
                    min: 0,
                    max: 10,
                    label: "Zip Files uploaded",
                    showInnerShadow: true,
                    shadowOpacity: 2,
                    shadowSize: 5,
                    refreshAnimationType: "bounce",
                    counter: true,
                    refreshAnimationTime: 3000
                });
            }
    
    
        </script>
    

    So NOTE close, our start button does NOT run server side code. It calls a routine that does a ajax call to the server side code. That way, no post-back!

    So, in our web page, we now create the web method:

    So, our code behind is now this:

        public class MyStatInfo
        {
            public int TotalFiles = 0;
            public int PdfFiles = 0;
            public int ZipFiles = 0;
        }
    
        [WebMethod]
        public static MyStatInfo GetStats()
        {
            // query code here - 
            SqlCommand cmdSQL = new SqlCommand("GetTestStats");
            cmdSQL.CommandType = CommandType.StoredProcedure;
            DateTime dtStart = new DateTime(2023, 1, 1);
            DateTime dtEnd = new DateTime(2023, 1, DateTime.DaysInMonth(2023, 1));
    
            cmdSQL.Parameters.Add("@dtStart",SqlDbType.Date).Value = dtStart;
            cmdSQL.Parameters.Add("@dtEnd",SqlDbType.Date).Value = dtEnd;
            DataTable dtResult = General.MyRstP2(cmdSQL);
    
            int TotalFiles = 0;
            foreach (DataRow dr in dtResult.Rows)
            {
                TotalFiles += (int)dr["FileCount"];
            }
    
            int PdfFiles = (int)dtResult.Rows[0]["FileCount"];
            int ZipFiles = (int)dtResult.Rows[1]["FileCount"];
    
            MyStatInfo stats = new MyStatInfo();
            stats.TotalFiles = TotalFiles;
            stats.PdfFiles = PdfFiles;
            stats.ZipFiles = ZipFiles;
    
            // fake 4 second delay
            System.Threading.Thread.Sleep(4000);
            return stats;
        }
    

    So, we call a sql stored procedure. All it does is return 2 rows (pdf file count, and zip file count). So, we "total" the 2.

    and since I wanted "easy" plucking out of the values client side, I shoved right into that web page code behind a class with the 3 values. (again, not a requirement, but it does make use of the values in client js code rather easy).

    As noted, that query runs VERY fast, so, I also tossed in a fake delay.

    The client side code thus starts a client side timer - and updates our one gauge.

    Then when the server side is done, it returns the values, I update the other 2 gauges, and stop the js timer.

    The results look like this: (and note if I re-run, the gauges have to be re-set!!!).

    So this:

    enter image description here

    Now, you don't have my SQL and stored procedure, so for a test, then use this:

    [WebMethod]
    public static MyStatInfo GetStats()
    {
        // query code here - 
    
        MyStatInfo stats = new MyStatInfo();
        stats.TotalFiles = 186;
        stats.PdfFiles = 117;
        stats.ZipFiles = 69;
    
        // fake 4 second delay
        System.Threading.Thread.Sleep(4000);
        return stats;
    }
    

    So, which approach is best? It not clear what the server side data you require.

    If some kind of "interval" say like reading some value every 10 seconds from the server would not suffice? Say one needs a real time update of some water in a tank? (and you want this to occur from the server side?

    Then for that type of dashboard and update system, then you have to bite the bullet so to speak, and adopt some technology stack that allows a "socket" like connection to the server, and thus you can update in real time as such data changes, and do so with server behind code. But, there are quite a few moving parts to SignalR, but that's what SignalR is designed for. So, most likely you can use some client-side JS code, and call a web method. Another possible would be to use a update panel, however, looking at the above results, I think calling a web method on the page looks to be a good start here.