Как вызвать асинхронный метод внутри задачи, созданной вручную?
Мне нужно реализовать что-то вроде объекта с холодным наблюдением в RxJS (просто стандартный Observable). Мне нужно вызвать асинхронный метод внутри задачи, которую я создаю сам с помощью конструктора (new Task()). Мне нужно реализовать это, потому что перед выполнением любого асинхронного кода я хочу сделать некоторые вещи, которые очень специфичны для моего проекта. Поэтому я хочу получить задачу, которая еще не запущена и которую я могу запустить вручную чуть позже.
До сих пор я пришел к следующему решению и, к моему удивлению, оно не работает!
class Program
{
static void Main(string[] args)
{
var task1 = CallApi(() => t.Go());
var task2 = CallApi2(() => t.Go());
task1.Start();
task2.Start();
}
public static Task<T> CallApi<T>(Func<Task<T>> function)
{
if (function == null)
{
throw new ArgumentNullException(nameof(function));
}
return new Task<Task<T>>(async () =>
{
return await function();
}).Unwrap();
}
public static Task<T> CallApi2<T>(Func<Task<T>> function)
{
if (function == null)
{
throw new ArgumentNullException(nameof(function));
}
var tcs = new TaskCompletionSource<T>();
var resultTask = new Task<Task<T>>(() =>
{
var t = function();
t.ContinueWith(
task => {
tcs.SetResult(task.Result);
},
TaskContinuationOptions.OnlyOnRanToCompletion
);
t.ContinueWith(
task => {
tcs.SetCanceled();
},
TaskContinuationOptions.OnlyOnCanceled
);
t.ContinueWith(
task => {
tcs.SetException(task.Exception);
},
TaskContinuationOptions.OnlyOnFaulted
);
return tcs.Task;
});
return resultTask.Unwrap();
}
}
Кажется, что вызов Unwrap или использование TaskCompletionSource создает задачу в состоянии WaitingForActivation. И вызов метода Start для задач в этом состоянии приводит меня к исключению, которое говорит:
Запуск может не вызываться для задачи в стиле обещания.
Поэтому весьма вероятно, что.NET выделяет особый вид задач - задачи в стиле обещаний.
В итоге мои вопросы:
Что означают эти задачи в стиле обещания?
Как я могу делать то, что хочу?
1 ответ
Задачи стиля Promise - это задачи, которые не основаны на потоках, они основаны на событиях, для TaskCompletionSource
"событие" - это акт вызова SetResult
, SetCanceled
или же SetException
Чтобы получить задачу, которая еще не запущена и которую можно запустить вручную чуть позже, просто удерживайте Func<Task<T>>
и вы оцениваете функцию для запуска задачи в более поздний момент времени. Это можно сделать довольно просто.
public void Example()
{
Func<Task<T>> func1 = () => t.Go();
//Do other work
Task<T> task1 = func1(); //t.Go() is not called until this point then the task starts.
}