Closed pavlexander closed 3 years ago
@pavlexander thanks for contacting us.
Blazor renders twice, the first time after all the synchronous work happens and the second when the asynchronous work completes. This ComponentBase behavior is by design.
@javiercn
@pavlexander thanks for contacting us.
Blazor renders twice, the first time after all the synchronous work happens and the second when the asynchronous work completes. This ComponentBase behavior is by design.
Thanks for the reply.. If you don't mind I would like to ask for some clarifications..
Since I use the await
in every place possible - doesn't this guarantee that the code is to be executed synchronously?
Second question - where does JSException
comes from?
I just want to understand the rules when the code will cause the re-rendering and when not..
I have done following modifications to the code:
protected async Task PreloadData()
{
Console.WriteLine($"Trigger PreloadData");
// this code will NOT cause re-rendering
//SetData();
// this call WILL cause re-rendering
//await SetData();
// this code will NOT cause re-rendering
await SetDataEmpty();
Console.WriteLine($"Finish PreloadData");
}
protected async Task SetData()
{
var vocabsResponse = await Http.GetFromJsonAsync<WeatherForecast[]>("WeatherForecast");
_preloadedObject = vocabsResponse.Count();
}
protected async Task SetDataEmpty()
{
}
If we are to use the SetData();
without await
- this would result in page NOT re-rendering. Hence, the expected behavior. The data will not be loaded because it's populated in another thread and not awaited in the main thread.
On the contrast, if we use the await SetData();
to force the code to execute synchronously, then the logic will continue to execute without awaiting
for the method to finish! And this is what I find controversial. Feels like this is not much about synchronicity but thread blocking issue.
See that I have added another method SetDataEmpty()
which we await
for.. This code will behave like I expect it to - it will NOT cause the re-rendering of the page, despite the fact that we have marked it as await
..
Since I use the
await
in every place possible - doesn't this guarantee that the code is to be executed synchronously?
Doesn't work that way. await
is (more or less) syntactic sugar for task.ContinueWith
so your code is more like
Task OnInitializedAsync()
{
// some stuff before await
return awaitedMethod().ContinueWith(() => { // Code after await });
}
The code that invokes OnInitializedAsync (in this case the framework) can decide to await the result of OnInitializedAsync (in which case it would serialize the execution) or can do other things before doing so.
In the case of ComponentBase, it decides to call StateHasChanged
at that point to trigger a render, and then manually checks if the task is completed and in case it is not, it does something like initResult.ContinueWith(t => { StateHasChanged();}) and that's why you see two renders.
protected async Task PreloadData()
{
Console.WriteLine($"Trigger PreloadData");
// this code will NOT cause re-rendering
//SetData(); <-- this is ignoring the result of the task, PreloadData will finish before SetData().Result is completed.
// this call WILL cause re-rendering
//await SetData(); <-- This schedules the rest of the code to execute after SetData has completed.
// this code will NOT cause re-rendering
await SetDataEmpty(); <-- This just returns a completed task.
Console.WriteLine($"Finish PreloadData");
}
In the three cases above the resulting task of calling PreloadData() is as follows: PreloadData().IsCompleted = true PreloadData().IsCompleted = false PreloadData().IsCompleted = true
If we are to use the
SetData();
withoutawait
- this would result in page NOT re-rendering. Hence, the expected behavior. The data will not be loaded because it's populated in another thread and not awaited in the main thread.
@javiercn
PreloadData().IsCompleted = false
If the framework 'decides' whether it should move on with the execution of the code or wait for the Task to finish then your explanation makes the perfect sense.. Is there a way to disable this behavior?
If I wanted to make the rendering logic explicit I would do the:
protected async Task SetData()
{
var vocabsResponse = await Http.GetFromJsonAsync<WeatherForecast[]>("WeatherForecast");
_preloadedObject = vocabsResponse.Count();
StateHasChanged(); // ADDED
}
and invoke the method as this: SetData();
.
Similarly if I wanted (want now) the framework to always wait for all the tasks to finish I would just continue to use the current implementation and invoke it like await SetData();
Do you think I can make a new feature request for this functionality? Or will it be insta-declined? :)
If the framework 'decides' whether it should move on with the execution of the code or wait for the Task to finish then your explanation makes the perfect sense.. Is there a way to disable this behavior?
You can override SetParametersAsync and implement your own logic there.
If I wanted to make the rendering logic explicit I would do the:
protected async Task SetData()
{
var vocabsResponse = await Http.GetFromJsonAsync<WeatherForecast[]>("WeatherForecast");
_preloadedObject = vocabsResponse.Count();
StateHasChanged(); // ADDED
}
and invoke the method as this: SetData();.
That wouldn't change anything, ComponentBase already calls StateHasChanged for you implicitly, and multiple render requests don't pile up.
Similarly if I wanted (want now) the framework to always wait for all the tasks to finish I would just continue to use the current implementation and invoke it like
await SetData();
You can do this if you override the logic to handle when parameters are set and when events are dispatched.
We don't plan to add a functionality to change when the components decide to render.
That wouldn't change anything, ComponentBase already calls StateHasChanged for you implicitly, and multiple render requests don't pile up.
Yes I understand that.. In that context I was already speaking of the feature
that will disable this default behavior and make the rendering logic explicit. Let's say you implement ExplicitRenderingComponentBase
that always waits for all tasks to finish execution before the first render. And then if I wanted to write a logic that needs to re-render the component - I would invoke the method asynchronously and call the StateHasChanged()
explicitly. (example in previous post)
Well, since you already mentioned that this feature is not wanted then I guess I will stick to using workarounds.. Thank yor for hints regarding SetParametersAsynt
. I will have a look.
One last topic that we have not resolved yet is the JSException
:
crit: Microsoft.AspNetCore.Components.WebAssembly.Rendering.WebAssemblyRenderer[100]
Unhandled exception rendering component: No element is currently associated with component 23
Error: No element is currently associated with component 23
It is being thrown after the re-rendering of the page. Despite the parameters already being set. Is this a bug/or something that needs to be fixing? Upon a second render parameter protected int? _preloadedObject;
is already set to non-null value.. we see the log message:
Trigger rendering. _preloadedObject=5
So the error probably has something to do with previous render failure and component not having a render tree? I would guess that the logic that pushes smart
/partial
updates to the page at this point should be aware of previous page failure and is not supposed to re-render a failed component. :) I will leave the decision of keeping this thread alive up to you. If you need more details from me on any of the topics mentioned in this thread please let me know.
When a component fails to render the current behavior is to tear down the application, since we consider the state of the application to be undefined at that point. Is the same thing as if you use the RenderTreeBuilder APIs and produce an unbalanced tree. We don't guarantee there are not going to be any errors or that the app can continue running.
fair enough :) I don't have a better proposal for error handling.. So perhaps this is OK.
Thanks for help!
Describe the bug
I am building a page that uses the base class to perform some common operations. The base class has the Initialization method the purpose of which is to pre-load the data to the page. However what I noticed is that page's initialization method is failing to await on the method in base class. (see code) part 1: This causes the page to throw the exception (
InvalidOperationException
) because objects are not initialized at the time of the first page render! part 2: Furthermore, after base classe's method finishes execution (continues from theawait
statement) - it causes page re-rendering. part 3: And finallyError: No element is currently associated with component 23
exception (JSException
) is thrown as if that was not enough :)To Reproduce
First create a fresh project in VisualStudio. Latest .Net 5 Blazor WASM (ASP.NET Core hosted) template.
Then create following classes:
MypageBase.cs
:Mypage.razor
:Mypage.razor.cs
Expected result (log)
Actual result (log)
Exception: Unhandled exception rendering component: Nullable object must have a value.
Exception: Unhandled exception rendering component: No element is currently associated with component 23
Further technical details
Lots of text ahead
.NET SDK (reflecting any global.json): Version: 5.0.103 Commit: 72dec52dbd Runtime Environment: OS Name: Windows OS Version: 10.0.19042 OS Platform: Windows RID: win10-x64 Base Path: C:\Program Files\dotnet\sdk\5.0.103\ Host (useful for support): Version: 5.0.3 Commit: c636bbdc8a .NET SDKs installed: 3.1.406 [C:\Program Files\dotnet\sdk] 5.0.102 [C:\Program Files\dotnet\sdk] 5.0.103 [C:\Program Files\dotnet\sdk] .NET runtimes installed: Microsoft.AspNetCore.All 2.1.24 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.All] Microsoft.AspNetCore.All 2.1.25 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.All] Microsoft.AspNetCore.App 2.1.24 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.App] Microsoft.AspNetCore.App 2.1.25 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.App] Microsoft.AspNetCore.App 3.1.11 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.App] Microsoft.AspNetCore.App 3.1.12 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.App] Microsoft.AspNetCore.App 5.0.2 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.App] Microsoft.AspNetCore.App 5.0.3 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.App] Microsoft.NETCore.App 2.1.24 [C:\Program Files\dotnet\shared\Microsoft.NETCore.App] Microsoft.NETCore.App 2.1.25 [C:\Program Files\dotnet\shared\Microsoft.NETCore.App] Microsoft.NETCore.App 3.1.11 [C:\Program Files\dotnet\shared\Microsoft.NETCore.App] Microsoft.NETCore.App 3.1.12 [C:\Program Files\dotnet\shared\Microsoft.NETCore.App] Microsoft.NETCore.App 5.0.2 [C:\Program Files\dotnet\shared\Microsoft.NETCore.App] Microsoft.NETCore.App 5.0.3 [C:\Program Files\dotnet\shared\Microsoft.NETCore.App] Microsoft.WindowsDesktop.App 3.1.11 [C:\Program Files\dotnet\shared\Microsoft.WindowsDesktop.App] Microsoft.WindowsDesktop.App 3.1.12 [C:\Program Files\dotnet\shared\Microsoft.WindowsDesktop.App] Microsoft.WindowsDesktop.App 5.0.2 [C:\Program Files\dotnet\shared\Microsoft.WindowsDesktop.App] Microsoft.WindowsDesktop.App 5.0.3 [C:\Program Files\dotnet\shared\Microsoft.WindowsDesktop.App] Microsoft Visual Studio Community 2019 Version 16.8.5 VisualStudio.16.Release/16.8.5+31005.135 Microsoft .NET Framework Version 4.8.04084 Installed Version: Community Visual C++ 2019 00435-60000-00000-AA596 Microsoft Visual C++ 2019 ASP.NET and Web Tools 2019 16.8.559.8768 ASP.NET and Web Tools 2019 ASP.NET Core Razor Language Services 16.1.0.2052803+84e121f1403378489b842e1797df2f3f5a49ac3c Provides languages services for ASP.NET Core Razor. ASP.NET Web Frameworks and Tools 2019 16.8.559.8768 For additional information, visit https://www.asp.net/ Azure App Service Tools v3.0.0 16.8.559.8768 Azure App Service Tools v3.0.0 Azure Functions and Web Jobs Tools 16.8.559.8768 Azure Functions and Web Jobs Tools C# Tools 3.8.0-5.20604.10+9ed4b774d20940880de8df1ca8b07508aa01c8cd C# components used in the IDE. Depending on your project type and settings, a different version of the compiler may be used. Common Azure Tools 1.10 Provides common services for use by Azure Mobile Services and Microsoft Azure Tools. IntelliCode Extension 1.0 IntelliCode Visual Studio Extension Detailed Info Markdown Editor 1.12.253 A full featured Markdown editor with live preview and syntax highlighting. Supports GitHub flavored Markdown. Microsoft Azure Tools 2.9 Microsoft Azure Tools for Microsoft Visual Studio 2019 - v2.9.30924.1 Microsoft Continuous Delivery Tools for Visual Studio 0.4 Simplifying the configuration of Azure DevOps pipelines from within the Visual Studio IDE. Microsoft JVM Debugger 1.0 Provides support for connecting the Visual Studio debugger to JDWP compatible Java Virtual Machines Microsoft Library Manager 2.1.113+g422d40002e.RR Install client-side libraries easily to any web project Microsoft MI-Based Debugger 1.0 Provides support for connecting Visual Studio to MI compatible debuggers Microsoft Visual C++ Wizards 1.0 Microsoft Visual C++ Wizards Microsoft Visual Studio Tools for Containers 1.1 Develop, run, validate your ASP.NET Core applications in the target environment. F5 your application directly into a container with debugging, or CTRL + F5 to edit & refresh your app without having to rebuild the container. Microsoft Visual Studio VC Package 1.0 Microsoft Visual Studio VC Package NuGet Package Manager 5.8.1 NuGet Package Manager in Visual Studio. For more information about NuGet, visit https://docs.nuget.org/ NVIDIA CUDA 10.1 Wizards 10.1 Wizards to create new NVIDIA CUDA projects and source files. NVIDIA CUDA 11.0 Wizards 11.0 Wizards to create new NVIDIA CUDA projects and source files. NVIDIA Nsight Visual Studio Edition 2020.1.2.20203 NVIDIA Nsight Visual Studio Edition provides tools for GPGPU and graphics development. Copyright © NVIDIA 2010 - 2020. •Direct3D® and DirectX® are registered trademarks of Microsoft Corporation in the United States and/or other countries. •Microsoft Detours is used under the Professional license (http://research.microsoft.com/en-us/projects/detours/). •Gardens Point Parser Generator Copyright 2005 Queensland University of Technology (QUT). All rights reserved. •Icons from Axialis Software used under the licensing terms found here: www.axialis.com •NLog Copyright © 2004-2006 Jaroslaw Kowalski (jaak@jkowalski.net) •zlib and libpng used under the zlib/libpnc license (http://opensource.org/licenses/Zlib) •Breakpad Copyright ©2006, Google Inc. All rights reserved. •The OpenGL Extension Wrangler Library Copyright ©2008-2016, Nigel Stewart (nigels@users.sourceforge.net), Copyright ©2002-2008, Milan Ikits (milan.ikits@ieee.org), Copyright ©2002-2008, Marcelo E. Magallon (mmagallo@debian.org), Copyright ©2002, Lev Povalahev. All rights reserved. •LIBSSH2 Copyright ©2004-2007 Sara Golemon (sarag@libssh2.org), Copyright ©2005,2006 Mikhail Gusarov (dottedmag@dottedmag.net),Copyright ©2006-2007 The Written Word, Inc.,Copyright ©2007 Eli Fant (elifantu@mail.ru),Copyright ©2009-2014 Daniel Stenberg., Copyright ©2008, 2009 Simon Josefsson. All rights reserved. •Protobuf Copyright ©2014, Google Inc. All rights reserved. •xxHASH Library Copyright ©2012-2014, Yann Collet. All rights reserved. •FMT Copyright ©2012 - 2016, Victor Zverovich •Font Awesome Copyright 2018 Fonticons, Inc. •ELF Definitions Copyright (c) 2010 Joseph Koshy, All rights reserved. Warning: This computer program is protected by copyright law and international treaties. Unauthorized reproduction or distribution of this program, or any portion of it, may result in severe civil and criminal penalties, and will be prosecuted to the maximum extent possible under the law. NVIDIA Nsight Visual Studio Edition - CUDA support 2020.1.2.20203 NVIDIA Nsight Visual Studio Edition - CUDA support provides tools for CUDA development and debugging. ProjectServicesPackage Extension 1.0 ProjectServicesPackage Visual Studio Extension Detailed Info SQL Server Data Tools 16.0.62102.01130 Microsoft SQL Server Data Tools Test Adapter for Boost.Test 1.0 Enables Visual Studio's testing tools with unit tests written for Boost.Test. The use terms and Third Party Notices are available in the extension installation directory. Test Adapter for Google Test 1.0 Enables Visual Studio's testing tools with unit tests written for Google Test. The use terms and Third Party Notices are available in the extension installation directory. TypeScript Tools 16.0.21016.2001 TypeScript Tools for Microsoft Visual Studio Visual Basic Tools 3.8.0-5.20604.10+9ed4b774d20940880de8df1ca8b07508aa01c8cd Visual Basic components used in the IDE. Depending on your project type and settings, a different version of the compiler may be used. Visual F# Tools 16.8.0-beta.20507.4+da6be68280c89131cdba2045525b80890401defd Microsoft Visual F# Tools Visual Studio Code Debug Adapter Host Package 1.0 Interop layer for hosting Visual Studio Code debug adapters in Visual Studio Visual Studio Container Tools Extensions 1.0 View, manage, and diagnose containers within Visual Studio. Visual Studio Tools for CMake 1.0 Visual Studio Tools for CMake Visual Studio Tools for Containers 1.0 Visual Studio Tools for Containers - ASP.NET Core version - Include the output of `dotnet --info` - The IDE (VS / VS Code/ VS4Mac) you're running on, and its versionFurther comments
Regardless if it's a bug or not - it would be helpful to know how exactly can I active the wished behavior? How can I force the class that implements the base class to wait until base-classes method finishes execution. Without rendering page twice, of course, if possible..