F#中异步IO的正确方法是什么

时间:2020-07-31 09:11:56

标签: asynchronous f#

假设我们具有以下C#方法

public async Task DoStuffAsync(string path)
{
    var fullPath = Path.Combine(
        Directory.GetCurrentDirectory(),
        path
    );

    using var stream = File.OpenText(fullPath);

    while (!stream.EndOfStream)
    {
        var line = await stream.ReadLineAsync();
                 
        await Console.Out.WriteLineAsync(line);
    }
}

我们想在F#中实现它,我的第一个尝试就是这样编写代码

let doStuffAsync path =
    async {
        let fullPath = Path.Combine(Directory.GetCurrentDirectory(), path)

        use stream = File.OpenText fullPath

        while (not stream.EndOfStream) do
            let! line = stream.ReadLineAsync() |> Async.AwaitTask
            System.Console.Out.WriteLineAsync(line) |> Async.AwaitTask |> ignore
    }

但是这对我来说不是正确的,因为我们必须将Task<string>解压缩为Async<string>,然后再解压缩。与Task中的WriteLineAsync相同。

方法ReadLineAsyncWriteLineAsync的F#变体是否返回Async<_>而不是Task<_>

还有某种异步管道操作员可以让我做

stream.ReadLineAsync() 
|> Async.AwaitTask
|> System.Console.Out.WriteLineAsync
|> Async.AwaitTask
|> ignore

1 个答案:

答案 0 :(得分:1)

正如@NghiaBui所指出的,使用Async.AwaitTask是正确的。这是两个异步模型,您需要在它们之间进行转换。

恐怕Async<'T>和大多数.net核心库都没有ReadlineAsync变体。

您可以使用TaskBuilder来使用一些更简洁的语法:

task {

  let fullPath = Path.Combine(Directory.GetCurrentDirectory(), path)

  use stream = File.OpenText fullPath
  while (not stream.EndOfStream) do
     let! line = stream.ReadLineAsync() 
     do! System.Console.Out.WriteLineAsync line      
}

关于管道,恐怕是不可能的。有一些discussions可以引入|>!运算符,但看起来它会增加不必要的复杂性。