这个异步操作应该怎么写?

2022-05-16 22:29:50 +08:00
 qtoq126
  1. c#/.net core webapi
  2. 这是一个 webAPI 的某个接口方法
  3. (步骤 1 )返回的是传给前端的 json 数据
  4. 在传递给前端数据后(步骤 2 ),还需要用这个数据去进行一个特别耗时的计算(步骤 3 )
  5. 现在我知道的是需要用异步去写步骤 2 ,但是如果加 await ,会一直等待他返回结果才会 return ,失去了 4 说的目的;但是如果不用 await ,方法直接 return 了,这个耗时操作方法就一直不会调用,好像也不行。
  6. 想请教下无所不知的 v 站大神们,感谢了

为什么编辑不能上传图了,附上图链接

1783 次点击
所在节点    程序员
21 条回复
DOLLOR
2022-05-16 22:34:42 +08:00
拆接口。
前端拿到 json 后,再次用那个 json 请求另一个接口来执行你那个“特别耗时的计算”,等待这个“特别耗时的计算”的同时,前端可以用这个 json 做其他的事情。
Buges
2022-05-16 22:54:18 +08:00
你的意思是接口返回的数据不需要等待计算完成,计算的结果不需要返回给前端?
那直接 spawn task 就好了。
qtoq126
2022-05-16 23:08:39 +08:00
@Buges 耗时的计算方法的返回结果不需要马上返回给前端,当他再去自己调用结果的时候,再给他
ration
2022-05-16 23:08:50 +08:00
1.后面的处理另开一个线程
2.使用消息队列
qtoq126
2022-05-16 23:10:56 +08:00
@DOLLOR 拆接口确实是个好方法,只是当拿到第一段 json 然后又马上进行二次请求的时候,前端页面是不是就会暂时卡死?我是希望前端拿到一段 json 后就可以开始展示页面了,用户该干嘛干嘛,然后我后台还在继续算,算完的结果也没必要马上通知给前端,而是当他再度调用到需要这个结果的功能的时候再返回给他
wunonglin
2022-05-16 23:35:33 +08:00
DOLLOR
2022-05-16 23:53:57 +08:00
@qtoq126 前端只要拿到第一次请求的 json 就可以开始渲染了,第二次请求是不会阻塞渲染的。
qtoq126
2022-05-16 23:56:28 +08:00
@DOLLOR 相当于用户可以任意操作,前端只是在后台默默请求?
qtoq126
2022-05-16 23:56:41 +08:00
@wunonglin ts 代码看不懂哇
DOLLOR
2022-05-16 23:58:28 +08:00
@qtoq126 对的,这本来就是异步请求的初衷。
Buges
2022-05-17 01:08:48 +08:00
@qtoq126 那就 spawn 到后台执行的同时返回一个 task id ,前端等待一段时间再用这个 id 请求第二个接口结果,如果尚未完成返回 pending 让前端等待一段时间再次请求。注意第二个接口只返回状态和结果而不要调用执行。
不单单拆接口,所有的接口处理函数中都不要同步等待结果维持连接。你想想你等待的时候连接超时了、中断了,或者用户刷新了重新发送。
Buges
2022-05-17 01:10:43 +08:00
所有接口都应该不要阻塞,尽快返回。前端无论如何都不应做“耗时”的请求,即使在后台。
qtoq126
2022-05-17 06:52:38 +08:00
@ration 重开一个线程的话,当我使用 return 返回第一段 json 后,主线程会不会就已经结束了啊?如果主线程已经结束了的话,Task.Run()运行的耗时函数可能还没拿到结果
qtoq126
2022-05-17 06:53:27 +08:00
@Buges 谢谢大佬点拨,你说的这个思路我能理解,就是具体到 api 的编写和异步函数的编写上面的话,就不知道怎么去做了
Buges
2022-05-17 08:00:05 +08:00
@qtoq126 返回只是结束你这个请求的 handler 函数啊,主程序和后面的线程池都是一直在运行的。除非你是 php 那样用 cgi 一个请求起一个进程。
具体实现很直接啊,第一个接口请求后 Task.Run 把耗时计算 spawn 到后台(不要 await ),并生成一个 UUID ,把这个 UUID 作为 key 和上面返回的 Task 存到一个共享的 ConcurrentDictionary 里面,并把这个 UUID 作为额外的字段加到 json 里返回给前端。
第二个接口前端拿这个 UUID 作为参数请求,拿到后通过这个 UUID 取得上面存的 Task 并检查 Task.Status 是否已经完成,未完成就返回 pending ,完成就返回结果(和释放资源)。

前端请求先第一个接口拿到 json 和 UUID 先渲染,然后等待一段时间在用 UUID 请求第二个接口,如果为 pending 那就等待一段时间重试直到拿到结果。
frisktale
2022-05-17 09:00:25 +08:00
如果你期望的是”执行步骤 3“且不关心返回值”,可以利用 Hangfile 库的 BackgroundJob.Enqueue 方法。
frisktale
2022-05-17 09:42:23 +08:00
不对啊,你说的第五点怎么和我印象里的不太一样。我记得,哪怕你不 await ,只要主程序不结束运行,你的异步方法还是会完整的执行下去的,只不过在异步方法的外部没法捕获异常。
frisktale
2022-05-17 10:25:55 +08:00
Feiir
2022-05-17 11:30:56 +08:00
成熟的方案就是队列
llwuuzz
2022-05-17 14:09:28 +08:00
伪代码:_=一个返回值为 Task 的方法;
这样就不会等待了,方法还是会执行的,也不需要用 Task.Run 之内的再封装了

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/853300

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX