如何判断ForEachAsync执行完成?gRpc流式调用场景下的实现问题
实现方案
你当前代码中直接用_ =丢弃了ForEachAsync返回的Task对象,因此无法监听遍历的完成状态,只要保留该Task并监听其状态即可实现需求:ForEachAsync本身返回的就是Task类型,该Task运行完成时就代表流式返回的所有元素已经遍历处理完毕(包括服务端主动结束流、触发取消、调用报错三种结束场景)。
代码修改示例
如果你需要等所有元素遍历完成后再执行后续操作,直接await这个Task即可:
private async Task UpdateProgress(string id) { CancellationTokenSource cts = new CancellationTokenSource(); ProgressServiceClient progressClient = new ProgressServiceClient(progressServerAddress); ChannelName channelName = new ChannelName() { Id = id }; var timestamp = Timestamp.FromDateTime(DateTime.UtcNow); // 保留ForEachAsync返回的Task,不要丢弃 var processTask = progressClient.ProgressReports(channelName) .ForEachAsync((x) => { if (timestamp < x.TimeStamp) { UpdateRow(x); } }, cts.Token); this.Dispatcher.Invoke(() => { if (cts != null && !cts.IsCancellationRequested) { Application.Current.Exit += (_, __) => cts.Cancel(); this.Unloaded += (_, __) => cts.Cancel(); } }); // 等待遍历结束,执行到此处就代表所有元素处理完成/触发取消/出现异常 try { await processTask; // 这里写遍历完成后要执行的后续业务逻辑 } catch (OperationCanceledException) { // 处理主动取消的场景:比如页面卸载、程序退出时触发的取消 } catch (Exception ex) { // 处理流式调用异常:比如网络中断、服务端报错等场景 } }
其他场景说明
如果你不需要同步等待遍历完成,希望先执行其他逻辑,之后再判断遍历是否结束,可以通过Task的属性直接判断状态:
processTask.IsCompleted:为true代表遍历已经结束(包含正常结束、取消、异常三种情况)processTask.IsCompletedSuccessfully:为true代表所有元素正常遍历处理完毕,没有触发取消也没有报错processTask.IsCanceled:为true代表遍历被CancellationToken主动取消processTask.IsFaulted:为true代表遍历过程中抛出了异常
另外你原代码末尾的await Task.Delay(50)没有实际业务作用,可以直接删除。
内容的提问来源于stack exchange,提问作者baerlein
相关产品推荐
相关产品推荐

