我是否正确地为 Task
实现了 map
?
let map continuation (t: Task<'A>) =
t.ContinueWith(fun (antecedent: Task<'A>) ->
if antecedent.Status <> TaskStatus.Canceled &&
antecedent.Status <> TaskStatus.Faulted then
continuation antecedent.Result
else
raise antecedent.Exception // must I?
)
我从 the docs 得到了 TaskStatus
检查.我对 raise antecedent.Exception
感到最不确定,但我想不出另一种方法来处理它。
作为背景,是的,我知道 Async
,但我当前的堆栈使用 Entity Framework 和 Blazor,所以我有一个后端使用 .ToListAsync()
> 和 C# 中的前端,所以我宁愿不处理从 Task
到 Async
然后再返回的转换。
最佳答案
我建议根据 TPL 中 awaitable
概念背后的接口(interface)来实现您的解决方案,即 INotifyCompletion
和 ICriticalNotifyCompletion
。此外,要正确实现 map
,您应该真正按照 bind
来实现。这在 F# 中已经有一些现有的解决方案,例如 TaskBuilder图书馆。就个人而言,多年来我一直在图书馆中使用以下内容,没有任何问题:
open System.Runtime.CompilerServices
open System.Threading.Tasks
type TaskStep<'result> =
| Value of 'result
| AsyncValue of 'result Task
| Continuation of ICriticalNotifyCompletion * (unit -> 'result TaskStep)
and StateMachine<'a>(firstStep) as this =
let methodBuilder = AsyncTaskMethodBuilder<'a Task>()
let mutable continuation = fun () -> firstStep
let nextAwaitable() =
try
match continuation() with
| Value r ->
methodBuilder.SetResult(Task.FromResult(r))
null
| AsyncValue t ->
methodBuilder.SetResult(t)
null
| Continuation (await, next) ->
continuation <- next
await
with
| exn ->
methodBuilder.SetException(exn)
null
let mutable self = this
member __.Run() =
methodBuilder.Start(&self)
methodBuilder.Task
interface IAsyncStateMachine with
member __.MoveNext() =
let mutable await = nextAwaitable()
if not (isNull await) then
methodBuilder.AwaitUnsafeOnCompleted(&await, &self)
member __.SetStateMachine(_) =
()
type Binder<'out> =
static member inline GenericAwait< ^abl, ^awt, ^inp
when ^abl : (member GetAwaiter : unit -> ^awt)
and ^awt :> ICriticalNotifyCompletion
and ^awt : (member get_IsCompleted : unit -> bool)
and ^awt : (member GetResult : unit -> ^inp) >
(abl : ^abl, continuation : ^inp -> 'out TaskStep) : 'out TaskStep =
let awt = (^abl : (member GetAwaiter : unit -> ^awt)(abl))
if (^awt : (member get_IsCompleted : unit -> bool)(awt))
then continuation (^awt : (member GetResult : unit -> ^inp)(awt))
else Continuation (awt, fun () -> continuation (^awt : (member GetResult : unit -> ^inp)(awt)))
module TaskStep =
let inline bind f step : TaskStep<'a> =
Binder<'a>.GenericAwait(step, f)
let inline toTask (step: TaskStep<'a>) =
try
match step with
| Value x -> Task.FromResult(x)
| AsyncValue t -> t
| Continuation _ as step -> StateMachine<'a>(step).Run().Unwrap()
with
| exn ->
let src = new TaskCompletionSource<_>()
src.SetException(exn)
src.Task
module Task =
let inline bind f task : Task<'a> =
TaskStep.bind f task |> TaskStep.toTask
let inline map f task : Task<'b> =
bind (f >> Value) task
关于f# - 如何实现Task.Map,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/59061122/