public virtual async Task BuildAsync(IBundleBuilderContext context) { // applying transforms to items var itemTransformTasks = new ConcurrentQueue <Task <IBundleItemTransformContext> >(); CancellationToken cancellationToken = context.CancellationToken; using (var errorCts = new CancellationTokenSource()) using (context.UseExternalCancellationToken(errorCts.Token)) { var n = context.Bundle.Sources.Length; for (var i = 0; i < n; i++) { cancellationToken.ThrowIfCancellationRequested(); IBundleSourceModel source = context.Bundle.Sources[i]; await source.ProvideBuildItemsAsync(context, it => itemTransformTasks.Enqueue(Task.Run(async() => { try { return(await ApplyItemTransformsAsync(it.ItemTransformContext, it.ItemTransforms)); } catch (Exception ex) when(!(ex is OperationCanceledException)) { errorCts.Cancel(); // stop processing other enqueued items throw; } }, cancellationToken))); } // "If any of the supplied tasks completes in a faulted state, the returned task will also complete in a Faulted state, where its exceptions will contain the aggregation of the set of unwrapped exceptions from each of the supplied tasks." // "If none of the supplied tasks faulted but at least one of them was canceled, the returned task will end in the Canceled state." // https://docs.microsoft.com/en-us/dotnet/api/system.threading.tasks.task.whenall?view=netcore-3.1 await Task.WhenAll(itemTransformTasks); } // aggregating items var transformContext = new BundleTransformContext(context) { TransformedItemContexts = itemTransformTasks.Select(task => task.GetAwaiter().GetResult()).ToArray() }; itemTransformTasks = null; await AggregateAsync(transformContext, context.Bundle.Transforms); // applying transforms to bundle transformContext.TransformedItemContexts = null; context.Result = await ApplyTransformsAsync(transformContext, context.Bundle.Transforms); }
public virtual async Task BuildAsync(IBundleBuilderContext context) { // building items CreateItemTransformPipeline(context, out ITargetBlock <IBundleSourceBuildItem> input, out ISourceBlock <IBundleItemTransformContext> output); // consumer async Task <List <IBundleItemTransformContext> > ConsumeAsync() { var itemContexts = new List <IBundleItemTransformContext>(); while (await output.OutputAvailableAsync(context.CancellationToken)) { itemContexts.Add(output.Receive()); } return(itemContexts); }; Task <List <IBundleItemTransformContext> > consumeTask = ConsumeAsync(); // producer var n = context.Bundle.Sources.Length; for (var i = 0; i < n; i++) { context.CancellationToken.ThrowIfCancellationRequested(); IBundleSourceModel source = context.Bundle.Sources[i]; await source.ProvideBuildItemsAsync(context, it => input.Post(it)); } input.Complete(); // building result var transformContext = new BundleTransformContext(context) { TransformedItemContexts = await consumeTask }; await AggregateAsync(transformContext, context.Bundle.Transforms); transformContext.TransformedItemContexts = null; context.Result = await ApplyTransformsAsync(transformContext, context.Bundle.Transforms); }