How to Async Files.ReadAllLines and await for results?

.net, async-await, asynchronous, c#, c#-5.0

Solution

UPDATE: Async versions of `File.ReadAll[Lines|Bytes|Text]`, `File.AppendAll[Lines|Text]` and `File.WriteAll[Lines|Bytes|Text]` have now been merged into .NET Core and shipped with .NET Core 2.0. They are also included in .NET Standard 2.1.

Using `Task.Run`, which essentially is a wrapper for `Task.Factory.StartNew`, for asynchronous wrappers is a code smell.

If you don't want to waste a CPU thread by using a blocking function, you should await a truly asynchronous IO method, `StreamReader.ReadToEndAsync`, like this:

using (var reader = File.OpenText("Words.txt"))
{
    var fileText = await reader.ReadToEndAsync();
    // Do something with fileText...
}

This will get the whole file as a `string` instead of a `List<string>`. If you need lines instead, you could easily split the string afterwards, like this:

using (var reader = File.OpenText("Words.txt"))
{
    var fileText = await reader.ReadToEndAsync();
    return fileText.Split(new[] { Environment.NewLine }, StringSplitOptions.None);
}

EDIT: Here are some methods to achieve the same code as `File.ReadAllLines`, but in a truly asynchronous manner. The code is based on the implementation of `File.ReadAllLines` itself:

using System.Collections.Generic;
using System.IO;
using System.Text;
using System.Threading.Tasks;

public static class FileEx
{
    /// <summary>
    /// This is the same default buffer size as
    /// <see cref="StreamReader"/> and <see cref="FileStream"/>.
    /// </summary>
    private const int DefaultBufferSize = 4096;

    /// <summary>
    /// Indicates that
    /// 1. The file is to be used for asynchronous reading.
    /// 2. The file is to be accessed sequentially from beginning to end.
    /// </summary>
    private const FileOptions DefaultOptions = FileOptions.Asynchronous | FileOptions.SequentialScan;

    public static Task<string[]> ReadAllLinesAsync(string path)
    {
        return ReadAllLinesAsync(path, Encoding.UTF8);
    }

    public static async Task<string[]> ReadAllLinesAsync(string path, Encoding encoding)
    {
        var lines = new List<string>();

        // Open the FileStream with the same FileMode, FileAccess
        // and FileShare as a call to File.OpenText would've done.
        using (var stream = new FileStream(path, FileMode.Open, FileAccess.Read, FileShare.Read, DefaultBufferSize, DefaultOptions))
        using (var reader = new StreamReader(stream, encoding))
        {
            string line;
            while ((line = await reader.ReadLineAsync()) != null)
            {
                lines.Add(line);
            }
        }

        return lines.ToArray();
    }
}

Problem

I have the following code, ``` private void button1_Click(object sender, RoutedEventArgs e) { button1.IsEnabled = false; var s = File.ReadAllLines("Words.txt").ToList(); // my WPF app hangs here // do something with s button1.IsEnabled = true; } ``` `Words.txt` has a ton of words which i read into the s variable, I am trying to make use of `async` and `await` keywords in C# 5 using `Async CTP Library` so the WPF app doesn't hang. So far I have the following code, ``` private async void button1_Click(object sender, RoutedEventArgs e) { button1.IsEnabled = false; Task<string[]> ws = Task.Factory.FromAsync<string[]>( // What do i have here? there are so many overloads ); // is this the right way to do? var s = await File.ReadAllLines("Words.txt").ToList(); // what more do i do here apart from having the await keyword? // do something with s button1.IsEnabled = true; } ``` The goal is to read the file in async rather than sync, to avoid freezing of WPF app. Any help is appreciated, Thanks!

Original source

Related problems