Aspnetcore: InvokeAsync freeze UI

Created on 19 Oct 2019  路  9Comments  路  Source: dotnet/aspnetcore

Describe the bug

Calling InvokeAsync

To Reproduce

Steps to reproduce the behavior:

  1. Using this version of ASP.NET Core 3.0
  2. Run this code
    List data = new List();
    for( int c = 0; c < 2000; c++ )
    data.Add( new { x = 5, y = 5, dt = 5 } );
    JsRuntime.InvokeAsync
  3. With these arguments ''
  4. See error
    -If You do anything like drawing something - the UI will freeze for some period
  5. Expected behavior

    since It's Async - it should not be freezing the UI

    Screenshots

    If applicable, add screenshots to help explain your problem.

    Additional context

    Add any other context about the problem here.
    Include the output of dotnet --info

    area-blazor blazor-wasm question

    Most helpful comment

    you can try to use Web Workers with javascript, which are basically background tasks that actually do what you want, but you'll need to write the background stuff in JS :(

    All 9 comments

    If this is client side blazor, javascript does not support background tasks with async.

    Yes, it is client side blazor.
    So, nothing can be done to stop this freezing ? :'(

    you can try to use Web Workers with javascript, which are basically background tasks that actually do what you want, but you'll need to write the background stuff in JS :(

    @YordanYanakiev Thanks for contacting us.

    You are performing an expensive operation on the UI thread. Id recommend you break that operation into multiple smaller chunks to avoid the freezing on the UI thread.

    Emh.. I am doing 3D complex rendering on the UI. Nothing more, nothing less. Anyway every 5 to 10 seconds about 100kb to 2 mb data is loaded from the server to the client, and it have to be transfered.
    It's quite complex situation, and there must be some sort of solution for something which is more than just a page with a buttons.
    So far - I am receiving the data from the server into the blazor project and then it have to send the data which is relative to the UI to the javascript where it should be additionally worker on and rendered. There is not much place for cutting the size, only some sort of threading will help ( as currently it is done on the desktop app which i am moving ).

    you can try to use Web Workers with javascript, which are basically background tasks that actually do what you want, but you'll need to write the background stuff in JS :(

    I am investigating it currently how to merge it with the blazor architecture.

    Update : Just found out that actually WebAssembly have a proposal which is in final stage, and working actually with multithreading.
    https://blog.scottlogic.com/2019/07/15/multithreaded-webassembly.html
    So, basically BLAZOR can go full Multithreading. [request]

    @YordanYanakiev That's a capability that needs to be exposed by the .NET runtime (mono in this case) before Blazor can even think of using it.

    Emh.. I am doing 3D complex rendering on the UI. Nothing more, nothing less. Anyway every 5 to 10 seconds about 100kb to 2 mb data is loaded from the server to the client, and it have to be transfered.
    It's quite complex situation, and there must be some sort of solution for something which is more than just a page with a buttons.
    So far - I am receiving the data from the server into the blazor project and then it have to send the data which is relative to the UI to the javascript where it should be additionally worker on and rendered. There is not much place for cutting the size, only some sort of threading will help ( as currently it is done on the desktop app which i am moving ).

    The problem here is that the marshalling between the C# side and the JavaScript side happens on the UI thread and that is a compute intensive task (that's why the UI freezes). This is a limitation in JavaScript itself and something all UI frameworks/apps have to deal with. My recommendation is that you design around it in a way similar to this.

    • From the C# side, make a JS interop call to tell the JS side you have data available, and pass a DotNetObjectReference to JS.
    • From the JS side perform a call to .NET to request a chunk of the data at a time while giving an opportunity to the UI thread to do other things.
    • When all the chunks are completed, post-process the data and complete your render.

    As an example, something like this should do the trick. setTimeout(fn,0) is used to let the UI loop perform work between retrieving chunks of data. You can make each chunk as small as you need to prevent freezing the UI thread.

    async function receiveData(reference){
      let [isFinal, next] = retrieveData(reference);
      let data = next;
    
      while(!isFinal){
        [isFinal, next] = await retrieveData(reference);
        appendData(data, next);
      }
    
      postProcess(data);
    }
    
    function retrieveData(reference){
      return new Promise((resolve, reject) => {
        setTimeout(() => {
          try{
            resolve(/* JSInterop to get more data "reference.readNextChunk()" */);
          }catch(e){
            reject(e);
          }
        },
        0);
      });
    }
    

    Your problem is not lack of multiple threads, but the fact that the work is compute intensive and happens on the UI thread, which is something that all JS frameworks share in common.

    Hope this helps.

    Was this page helpful?
    0 / 5 - 0 ratings