C# ASP.NET MVC异步调用OpenAI及视图更新问题咨询
问题解答
一、现有代码是否实现了异步并行?
你的代码是异步但串行的,并没有实现并行调用。
原因:在foreach循环中,每次调用CompleteChatAsync获取任务后立刻await chatResult,这会导致当前线程等待该任务完成后,才会进入下一次循环发起下一个请求。所有OpenAI请求是依次执行的,并没有同时发起。
正确的并行异步实现方式
先收集所有异步任务,再一次性等待全部完成,示例代码如下:
// 1. 收集所有异步任务 var tasks = new List<Task<ClientResult<ChatCompletion>>>(); foreach (ChatMessage msg in messages) { List<ChatMessage> tmpMessage = new List<ChatMessage> { msg }; tasks.Add(client.CompleteChatAsync(tmpMessage, chatOptions)); } // 2. 等待所有任务并行完成 await Task.WhenAll(tasks); // 3. 处理所有结果 foreach (var task in tasks) { if (task.IsCompletedSuccessfully && task.Result.Value != null) { output.Add(task.Result.Value.Content[0].Text); } }
注意:IsCompletedSuccessfully比IsCompleted更严谨,能排除任务失败或取消的情况。
二、ASP.NET MVC中如何解决视图不更新、一直等待的问题?
ASP.NET MVC默认是服务器端渲染模式,必须等Action中的所有逻辑完成后,才会生成完整视图返回给浏览器,所以会出现一直等待的情况。要实现实时刷新视图,需要结合前端异步或实时通信技术,以下是几种可行方案:
方案1:前端AJAX异步请求(最简单)
- 后端:编写一个
ApiController,提供单个处理OpenAI请求的接口:
public class OpenAIController : ApiController { public async Task<IHttpActionResult> ProcessMessage([FromBody] ChatMessage msg) { var tmpMessage = new List<ChatMessage> { msg }; var result = await client.CompleteChatAsync(tmpMessage, chatOptions); if (result.Value != null) { return Ok(result.Value.Content[0].Text); } return BadRequest("请求失败"); } }
- 前端:在页面中用JavaScript循环发起AJAX请求,每个请求完成后立即更新页面内容:
// 假设messages是前端的消息数组 const messages = [/* 你的消息数据 */]; const resultContainer = document.getElementById('result-container'); messages.forEach(async (msg) => { const response = await fetch('/api/OpenAI/ProcessMessage', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(msg) }); const text = await response.text(); const div = document.createElement('div'); div.textContent = text; resultContainer.appendChild(div); });
方案2:Server-Sent Events (SSE) 服务器推送
适合单向实时推送场景,服务器完成一个任务就向前端推送一次结果:
- 后端:编写一个返回
EventStream的Action:
public async Task ProcessMessages(List<ChatMessage> messages) { Response.ContentType = "text/event-stream"; Response.BufferOutput = false; foreach (ChatMessage msg in messages) { var tmpMessage = new List<ChatMessage> { msg }; var result = await client.CompleteChatAsync(tmpMessage, chatOptions); if (result.Value != null) { await Response.WriteAsync($"data: {JsonConvert.SerializeObject(result.Value.Content[0].Text)}\n\n"); await Response.FlushAsync(); } } }
- 前端:监听SSE事件,实时更新页面:
const eventSource = new EventSource('/Home/ProcessMessages'); const resultContainer = document.getElementById('result-container'); eventSource.onmessage = function(event) { const text = event.data; const div = document.createElement('div'); div.textContent = text; resultContainer.appendChild(div); }; eventSource.onerror = function() { eventSource.close(); };
方案3:SignalR双向通信
适合更复杂的实时交互场景,支持服务器和客户端双向通信:
- 安装SignalR NuGet包:
Install-Package Microsoft.AspNet.SignalR - 创建Hub类:
public class OpenAIHub : Hub { public async Task ProcessMessages(List<ChatMessage> messages) { foreach (ChatMessage msg in messages) { var tmpMessage = new List<ChatMessage> { msg }; var result = await client.CompleteChatAsync(tmpMessage, chatOptions); if (result.Value != null) { await Clients.Caller.SendAsync("ReceiveResult", result.Value.Content[0].Text); } } } }
- 前端连接Hub并接收结果:
const connection = $.hubConnection(); const openAIHub = connection.createHubProxy('openAIHub'); openAIHub.on('ReceiveResult', function(text) { const div = document.createElement('div'); div.textContent = text; document.getElementById('result-container').appendChild(div); }); connection.start().done(function() { // 发送消息列表到服务器 openAIHub.invoke('ProcessMessages', [/* 消息数组 */]); });
内容的提问来源于stack exchange,提问作者Zach
相关产品推荐
相关产品推荐

