Skip to content

[Components] Adds support for rendering async components - #6708

Merged
mkArtakMSFT merged 1 commit into
masterfrom
javiercn/components-async
Jan 17, 2019
Merged

mkArtakMSFT merged 1 commit into
masterfrom
javiercn/components-async

Conversation

@javiercn

Copy link
Copy Markdown
Member
  • Converts SetParameters to SetParametersAsync
  • Captures all the returned task produced by SetParametersAsync of each component to properly await them.
  • Introduces RenderRootComponentAsync to render the root component and deterministically wait for all async work to be done.
  • Protects the state in Renderer from concurrent modifications.

Comment thread src/Components/src/Microsoft.AspNetCore.Components/IComponent.cs
Comment thread src/Components/src/Microsoft.AspNetCore.Components/CascadingValue.cs Outdated
Comment thread src/Components/src/Microsoft.AspNetCore.Components/ComponentBase.cs Outdated
Comment thread src/Components/src/Microsoft.AspNetCore.Components/Rendering/ComponentState.cs Outdated
@vertonghenb

Copy link
Copy Markdown
Contributor

Could this fix the following issue? #5592 (comment)

@rynowak

rynowak commented Jan 15, 2019

Copy link
Copy Markdown
Contributor

Question I forgot to ask elsewhere, how will this handle encoding?

@javiercn

Copy link
Copy Markdown
Member Author

Question I forgot to ask elsewhere, how will this handle encoding?

@rynowak The HTML renderer gets a Func<string,string> encoder that does the encoding. Due to the fact that this lives in Microsoft.AspNetCore.Components and we don't want to bring a dependency to the webencoders in that package.

Mvc just provides the htmlEncoder resolved from DI

Comment thread src/Components/src/Microsoft.AspNetCore.Components/ParameterCollection.cs Outdated
@rynowak

rynowak commented Jan 15, 2019

Copy link
Copy Markdown
Contributor

Ok cool beans

@javiercn
javiercn force-pushed the javiercn/components-async branch from d2577cd to a67f314 Compare January 15, 2019 23:20
Comment thread src/Components/src/Microsoft.AspNetCore.Components/Rendering/ComponentState.cs Outdated
Comment thread src/Components/src/Microsoft.AspNetCore.Components/CascadingValue.cs Outdated
Comment thread src/Components/src/Microsoft.AspNetCore.Components/ComponentBase.cs Outdated
Comment thread src/Components/src/Microsoft.AspNetCore.Components/IComponent.cs
Comment thread src/Components/src/Microsoft.AspNetCore.Components/Rendering/Renderer.cs Outdated
private int _nextComponentId = 0; // TODO: change to 'long' when Mono .NET->JS interop supports it
private bool _isBatchInProgress;
private int _lastEventHandlerId = 0;
private List<Task> _pendingTasks = new List<Task>();

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I might be missing something, but it looks like in the non-HtmlRenderer case, this list will grow infinitely and is never cleared after the initial render.

Should we do something so that only HtmlRenderer actually populates this list, and the others do nothing in AddToPendingTasks? The AddToPendingTasks could be changed to a virtual method that no-ops in the default case.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This gets cleaned up in all code paths as

void RenderRootComponent(int componentId, ParameterCollection initialParameters)
{
            ReportAsyncExceptions(RenderRootComponentAsync(componentId, initialParameters));
}

Internally makes sure that all the cleanup happens.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I thought that only happened on the first render cycle. When some event like a button click happens later in the other renderers, this code path doesn't execute, does it?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah, I see what you mean. You're right. But it has an easy fix

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I null the list after RenderRootComponentAsync is done

Comment thread src/Components/src/Microsoft.AspNetCore.Components/Rendering/Renderer.cs Outdated

@SteveSandersonMS SteveSandersonMS left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looking great! Hopefully the remaining points won't be hugely disruptive to resolve. Thanks for tracking down so many fine details about the lifecycle method execution process.

@javiercn

Copy link
Copy Markdown
Member Author

Updated

// reason we have OnAfterRenderAsync is so that the developer doesn't
// have to use "async void" and do their own exception handling in
// the case where they want to start an async task.
var taskWithHandledException = HandleAfterRenderException(onAfterRenderTask);

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks like we don't need the taskWithHandledException var, or do we?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The compiler complains if we take it out

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

_ = HandleAfterRenderException(...);

@SteveSandersonMS SteveSandersonMS left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Excellent!

* Updates the IComponent interface to rename Init into Configure
* Updates the IComponent interface to change SetParameters for
  SetParametersAsync and make it return a Task that represents when the
  component is done applying the parameters and potentially triggering
  one or more renders.
* Updates ComponentBase SetParametersAsync to ensure that OnInit(Async)
  runs before OnParametersSet(Async).
* Introduces ParameterCollection.FromDictionary to generate a parameter
  collection from a dictionary of key value pairs.
* Introduces RenderComponentAsync on HtmlRenderer to support
  prerrendering of async components.
* Introduces RenderRootComponentAsync on the renderer to allow for
  asynchronous prerrendering of the root component.
@javiercn
javiercn force-pushed the javiercn/components-async branch from e2c8696 to e7892e1 Compare January 17, 2019 07:42
@javiercn

Copy link
Copy Markdown
Member Author

This is blocked waiting on this unrelated failing test:
2019-01-17T09:17:29.8475061Z [xUnit.net 00:00:50.51] Microsoft.DotNet.Watcher.Tools.Tests.MsBuildFileSetFactoryTest.FindsCustomWatchItems [FAIL]
2019-01-17T09:17:32.4442223Z Failed Microsoft.DotNet.Watcher.Tools.Tests.MsBuildFileSetFactoryTest.FindsCustomWatchItems

@natemcmaster Is there something we can do here? I've triggered several builds already

@javiercn
javiercn force-pushed the javiercn/components-async branch from d2bf15b to e7892e1 Compare January 17, 2019 13:53
@rynowak

rynowak commented Jan 17, 2019

Copy link
Copy Markdown
Contributor

If the flaky test is blocking you, log an issue and mark it as skipped.

@mkArtakMSFT
mkArtakMSFT merged commit 19b543e into master Jan 17, 2019
@mkArtakMSFT

mkArtakMSFT commented Jan 17, 2019

Copy link
Copy Markdown
Contributor

Merging in as this is blocked on an unrelated test.

@natemcmaster
natemcmaster deleted the javiercn/components-async branch January 18, 2019 19:16
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

7 participants