I have a variable number of tasks defined in Func<Task>
types so I can define the tasks but not have them automatically start. Say, for example:
Func<Task> task1Func = () => DoTask1Async();
Some of the tasks defined in such a way return values. I need a way to 'pass' the result of such tasks to arguments of subsequent tasks, defined in the same way. So, I would like to write:
Func<Task> task2Func = () => DoTask2Async(task1Func.Invoke().Result));
The trouble with this is that it the task1Func.Invoke()
part starts the task1 task again which is no good.
I can't reframe the input to chain continuations like:
Func<Task> funcTasks = () => DoTask1Async().ContinueWith(t => DoTask2Async(t.Result));
as I need to know in code the point at which each task (i.e. DoTask1Async / DoTask2Async) in the chain completes to run some other code. Another problem with this is that I don't know a way to compute how many tasks are involved in the 'chain'.
I can't store the task results as they complete as the tasks need to be defined declaratively like task1Func
and task2Func
.
The tasks are processed in a defined order, with each task completing before the next is processed. Results from tasks are used for subsequent tasks only.
Edit
In response to request for runnable code using task1Func.Invoke().Result
here it is. Create a new WPF project and add a button to the default Grid. Then clear the MainWindow.xaml.cs file and paste in what's below. This is the closest code to my real project, while cutting out all of the things irrelevant to the issue.
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using System.Windows;
namespace WpfApplication2
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
private async void Button_Click(object sender, RoutedEventArgs e)
{
// Declare first task that returns a result
Func<Task<int>> deferredTask1 = () => DoTask1Async();
// Declare second task using result of first.
// BUT this isn't good as it calls deferredTask1 AGAIN, so need another approach.
Func<Task> deferredTask2 = () => DoTask2Async(deferredTask1.Invoke().Result);
var item1 = new MyItem() { MyDeferredTask = deferredTask1 };
var item2 = new MyItem() { MyDeferredTask = deferredTask2 };
List<MyItem> items = new List<MyItem>();
items.Add(item1);
items.Add(item2);
await DoTasksAsync(items);
}
public static async Task<int> DoTask1Async()
{
return await Task<int>.Run(() => { return 3000; });
}
public static async Task DoTask2Async(int delay)
{
await Task.Delay(delay);
}
private async Task DoTasksAsync(IEnumerable<MyItem> items)
{
foreach (var item in items)
{
await item.MyDeferredTask();
}
}
private class MyItem
{
public Func<Task> MyDeferredTask { get; set; }
}
}
}