Skip to content

Commit e768a78

Browse files
Ensure ComponentBase async lifecycle methods return non-null task (imported from Blazor PR 1620) (#4790)
1 parent 8a9df6c commit e768a78

File tree

3 files changed

+369
-23
lines changed

3 files changed

+369
-23
lines changed

src/Components/src/Microsoft.AspNetCore.Components/ComponentBase.cs

Lines changed: 34 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -158,34 +158,51 @@ public virtual void SetParameters(ParameterCollection parameters)
158158
_hasCalledInit = true;
159159
OnInit();
160160

161-
// If you override OnInitAsync and return a nonnull task, then by default
161+
// If you override OnInitAsync and return a noncompleted task, then by default
162162
// we automatically re-render once that task completes.
163163
var initTask = OnInitAsync();
164-
if (initTask != null && initTask.Status != TaskStatus.RanToCompletion)
165-
{
166-
initTask.ContinueWith(ContinueAfterLifecycleTask);
167-
}
164+
ContinueAfterLifecycleTask(initTask);
168165
}
169166

170167
OnParametersSet();
171168
var parametersTask = OnParametersSetAsync();
172-
if (parametersTask != null && parametersTask.Status != TaskStatus.RanToCompletion)
173-
{
174-
parametersTask.ContinueWith(ContinueAfterLifecycleTask);
175-
}
169+
ContinueAfterLifecycleTask(parametersTask);
176170

177171
StateHasChanged();
178172
}
179173

180-
private void ContinueAfterLifecycleTask(Task task)
174+
private async void ContinueAfterLifecycleTask(Task task)
181175
{
182-
if (task.Exception == null)
183-
{
184-
StateHasChanged();
185-
}
186-
else
176+
switch (task == null ? TaskStatus.RanToCompletion : task.Status)
187177
{
188-
HandleException(task.Exception);
178+
// If it's already completed synchronously, no need to await and no
179+
// need to issue a further render (we already rerender synchronously).
180+
// Just need to make sure we propagate any errors.
181+
case TaskStatus.RanToCompletion:
182+
case TaskStatus.Canceled:
183+
break;
184+
case TaskStatus.Faulted:
185+
HandleException(task.Exception);
186+
break;
187+
188+
// For incomplete tasks, automatically re-render on successful completion
189+
default:
190+
try
191+
{
192+
await task;
193+
StateHasChanged();
194+
}
195+
catch (Exception ex)
196+
{
197+
// Either the task failed, or it was cancelled, or StateHasChanged threw.
198+
// We want to report task failure or StateHasChanged exceptions only.
199+
if (!task.IsCanceled)
200+
{
201+
HandleException(ex);
202+
}
203+
}
204+
205+
break;
189206
}
190207
}
191208

@@ -203,18 +220,12 @@ private static void HandleException(Exception ex)
203220
void IHandleEvent.HandleEvent(EventHandlerInvoker binding, UIEventArgs args)
204221
{
205222
var task = binding.Invoke(args);
223+
ContinueAfterLifecycleTask(task);
206224

207225
// After each event, we synchronously re-render (unless !ShouldRender())
208226
// This just saves the developer the trouble of putting "StateHasChanged();"
209227
// at the end of every event callback.
210228
StateHasChanged();
211-
212-
if (task.Status == TaskStatus.RanToCompletion)
213-
{
214-
return;
215-
}
216-
217-
task.ContinueWith(ContinueAfterLifecycleTask);
218229
}
219230

220231
void IHandleAfterRender.OnAfterRender()

0 commit comments

Comments
 (0)