12

我一直在玩 Blazor 和 C# 8.0 中的 IAsyncEnumerable 功能。是否可以在 Razor 页面中使用 IAsyncEnumerable 和 await 来逐步显示带有数据的标记?

示例服务:

private static readonly string[] games = new[] { "Call of Duty", "Legend of Zelda", "Super Mario 64" };
public async IAsyncEnumerable<string> GetGames()
{
   foreach (var game in games)
   {
     await Task.Delay(1000);
     yield return game;
   }
}

剃刀页面中的示例:

@await foreach(var game in GameService.GetGames())
{
  <p>@game</p>
}

这会产生错误 CS4033:“等待”运算符只能在异步方法中使用。考虑使用“异步”修饰符标记此方法并将其返回类型更改为“任务”。

如果可能的话,有什么想法吗?

4

2 回答 2

12

服务器端 Razor 允许您描述的内容。该视频介绍了此 Github存储库中的代码,该代码展示了如何通过修改ForecastService服务器端 Blazor 模板中的示例来使用 IAsyncEnumerable。

修改服务本身很容易,实际上会产生更简洁的代码:

    public async IAsyncEnumerable<WeatherForecast> GetForecastAsync(DateTime startDate)
    {
        var rng = new Random();
        for(int i=0;i<5;i++)
        {
            await Task.Delay(200);
            yield return new WeatherForecast
            {
                Date = startDate.AddDays(i),
                TemperatureC = rng.Next(-20, 55),
                Summary = Summaries[rng.Next(Summaries.Length)]
            };
        }
    }

另一方面,Blazor 页面更复杂。不仅循环必须在 HTML 显示之前完成,您不能在页面本身中使用await foreach,因为它不是异步的。您只能在代码块中定义异步方法。

您可以做的是枚举 IAsyncEnumerable 并通知页面在每次更改后自行刷新。

渲染代码本身不需要更改:

    <table class="table">
        <thead>
            <tr>
                <th>Date</th>
                <th>Temp. (C)</th>
                <th>Temp. (F)</th>
                <th>Summary</th>
            </tr>
        </thead>
        <tbody>
            @foreach (var forecast in forecasts)
            {
                <tr>
                    <td>@forecast.Date.ToShortDateString()</td>
                    <td>@forecast.TemperatureC</td>
                    <td>@forecast.TemperatureF</td>
                    <td>@forecast.Summary</td>
                </tr>
            }
        </tbody>
    </table>

OnInitializeAsyncStateHasChanged()收到每件物品后需要致电:

    List<WeatherForecast> forecasts;

    protected override async Task OnInitializedAsync()
    {
        forecasts =new List<WeatherForecast>(); 
        await foreach(var forecast in ForecastService.GetForecastAsync(DateTime.Now))
        {
            forecasts.Add(forecast);
            this.StateHasChanged();            
        }
    }

在问题的示例中,传入的游戏可以存储在 List 中,而渲染代码保持不变:

@foreach(var game in games)
{
  <p>@game</p>
}

@code {
    List<string> games;

    protected override async Task OnInitializedAsync()
    {
        games =new List<games>(); 
        await foreach(var game in GameService.GetGamesAsync())
        {
            games.Add(game);
            this.StateHasChanged();            
        }
    }
}
于 2019-09-10T13:51:56.850 回答
5

你不能写await foreach模板.razor代码。但是,作为解决方法,您可以在以下部分编写它@code

@if (@gamesUI == null)
{
    <p><em>Loading...</em></p>
}
else
{
    <table class="table">
        <thead>
            <tr>
                <th>Game</th>
            </tr>
        </thead>
        <tbody>
            @foreach (var game in gamesUI)  // <--- workaround
            {
                <tr>
                    <td>@game</td>
                </tr>
            }
        </tbody>
    </table>
}

@code {
    List<string> gamesUI;  // <--- workaround

    protected override async Task OnInitializedAsync()
    {
        gamesUI = new List<string>();
        await foreach(var game in GService.GetgamesAsync() )
        {
            gamesUI.Add(game);
            this.StateHasChanged();
        }
    }
}

影响:

在此处输入图像描述

产量数据:

        private static readonly string[] games = new[] { "Call of Duty", "Legend of Zelda", "Super Mario 64", "Bag man" };


        public async IAsyncEnumerable<string> GetgamesAsync()
        {
            var rng = new Random();

            foreach (var game in games)
            {
                await Task.Delay(1000);
                yield return game;
            }
        }
于 2019-09-10T14:11:46.557 回答