private async Task RunOnNavigateAsync(string path) { // If this router instance does not provide an OnNavigateAsync parameter // then we render the component associated with the route as per usual. if (!OnNavigateAsync.HasDelegate) { return; } // If we've already invoked a task and stored its CTS, then // cancel the existing task. _onNavigateCts?.Dispose(); // Create a new cancellation token source for this instance _onNavigateCts = new CancellationTokenSource(); var navigateContext = new NavigationContext(path, _onNavigateCts.Token); // Create a cancellation task based on the cancellation token // associated with the current running task. var cancellationTaskSource = new TaskCompletionSource(); navigateContext.CancellationToken.Register(state => ((TaskCompletionSource)state).SetResult(), cancellationTaskSource); var task = OnNavigateAsync.InvokeAsync(navigateContext); // If the user provided a Navigating render fragment, then show it. if (Navigating != null && task.Status != TaskStatus.RanToCompletion) { _renderHandle.Render(Navigating); } await Task.WhenAny(task, cancellationTaskSource.Task); }
internal async ValueTask RunOnNavigateAsync(string path, bool isNavigationIntercepted) { // Cancel the CTS instead of disposing it, since disposing does not // actually cancel and can cause unintended Object Disposed Exceptions. // This effectivelly cancels the previously running task and completes it. _onNavigateCts?.Cancel(); // Then make sure that the task has been completely cancelled or completed // before starting the next one. This avoid race conditions where the cancellation // for the previous task was set but not fully completed by the time we get to this // invocation. await _previousOnNavigateTask; var tcs = new TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously); _previousOnNavigateTask = tcs.Task; var okToRoute = this.RouterSessionService.CanRoute(this.NavigationManager.Uri, _locationAbsolute, out bool reRoute); // Need to check if we can route if (!OnNavigateAsync.HasDelegate && okToRoute) { Refresh(isNavigationIntercepted); } _onNavigateCts = new CancellationTokenSource(); var navigateContext = new NavigationContext(path, _onNavigateCts.Token); var cancellationTcs = new TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously); navigateContext.CancellationToken.Register(state => ((TaskCompletionSource)state).SetResult(), cancellationTcs); try { // Task.WhenAny returns a Task<Task> so we need to await twice to unwrap the exception var task = await Task.WhenAny(OnNavigateAsync.InvokeAsync(navigateContext), cancellationTcs.Task); await task; tcs.SetResult(); // Need to check if we can route if (okToRoute) { Refresh(isNavigationIntercepted); } } catch (Exception e) { _renderHandle.Render(builder => ExceptionDispatchInfo.Throw(e)); } // if reroute then do it and exit if (reRoute) { await ReNavigateAsync(); } }
internal async ValueTask RunOnNavigateAsync(string path, bool isNavigationIntercepted) { Logger.LogDebug("RunOnNavigateAsync with path {path}", path); // Cancel the CTS instead of disposing it, since disposing does not // actually cancel and can cause unintended Object Disposed Exceptions. // This effectivelly cancels the previously running task and completes it. _onNavigateCts?.Cancel(); // Then make sure that the task has been completely cancelled or completed // before starting the next one. This avoid race conditions where the cancellation // for the previous task was set but not fully completed by the time we get to this // invocation. await _previousOnNavigateTask; var tcs = new TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously); _previousOnNavigateTask = tcs.Task; if (!OnNavigateAsync.HasDelegate) { Logger.LogDebug("RunOnNavigateAsync - !OnNavigateAsync.HasDelegate"); await Refresh(isNavigationIntercepted); } _onNavigateCts = new CancellationTokenSource(); var navigateContext = new NavigationContext(path, _onNavigateCts.Token); var cancellationTcs = new TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously); navigateContext.CancellationToken.Register(state => ((TaskCompletionSource)state).SetResult(), cancellationTcs); try { // Task.WhenAny returns a Task<Task> so we need to await twice to unwrap the exception var task = await Task.WhenAny(OnNavigateAsync.InvokeAsync(navigateContext), cancellationTcs.Task); await task; tcs.SetResult(); Logger.LogDebug("RunOnNavigateAsync - Refresh(isNavigationIntercepted)"); await Refresh(isNavigationIntercepted); } catch (Exception e) { Logger.LogError(e, "RunOnNavigateAsync - Catch"); _renderHandle.Render(builder => ExceptionDispatchInfo.Throw(e)); } }
private async ValueTask <bool> RunOnNavigateAsync(string path, Task previousOnNavigate) { // If this router instance does not provide an OnNavigateAsync parameter // then we render the component associated with the route as per usual. if (!OnNavigateAsync.HasDelegate) { return(true); } // If we've already invoked a task and stored its CTS, then // cancel that existing CTS. _onNavigateCts?.Cancel(); // Then make sure that the task has been completed cancelled or // completed before continuing with the execution of this current task. await previousOnNavigate; // Create a new cancellation token source for this instance _onNavigateCts = new CancellationTokenSource(); var navigateContext = new NavigationContext(path, _onNavigateCts.Token); // Create a cancellation task based on the cancellation token // associated with the current running task. var cancellationTcs = new TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously); navigateContext.CancellationToken.Register(state => ((TaskCompletionSource)state).SetResult(), cancellationTcs); var task = OnNavigateAsync.InvokeAsync(navigateContext); // If the user provided a Navigating render fragment, then show it. if (Navigating != null && task.Status != TaskStatus.RanToCompletion) { _renderHandle.Render(Navigating); } var completedTask = await Task.WhenAny(task, cancellationTcs.Task); return(task == completedTask); }