扣子异步任务处理:高耗时工作流轮询设计与实现指南
首先要明确一个核心结论:轮询,才是异步任务唯一稳定且可靠的获取结果方式。扣子平台目前不支持 WebSocket 或 SSE 回调,想要拿到结果,就得靠 GET /task/{id}/status 接口不断轮询。响应里会返回标准状态和进度信息,而请求频率的控制策略——指数退避——是避免把后端打崩的关键。任务一旦完成或失败,必须及时终止轮询,别让无效请求继续跑下去。

当扣子工作流要执行图像生成、视频渲染或者批量数据处理这类耗时超过 5 分钟的任务时,同步等待模式必须放弃。否则用户会直接看到“任务处理超时”并中断流程,后台任务其实还在跑,结果却没人知道。
为什么轮询是异步任务的必选项
异步任务提交后,工作流会立即返回一个任务 ID,但系统不会主动把结果推给你。不轮询,你就永远不知道任务到底完成了、失败了还是卡死了。轮询不是“多此一举”,而是唯一能闭环的关键链路——就像快递已经发出,你得自己查物流,而不是干等门铃响。
扣子平台不支持 WebSocket 推送或 SSE 回调,外部渠道(比如飞书、微信)更不可能有回调能力。
轮询,是当前唯一稳定可靠的获取结果方式。
设计轮询接口:三要素缺一不可
第一步:在你的后端服务里暴露一个 GET /task/{id}/status 接口,返回标准 JSON 结构。比如 {"status":"RUNNING","progress":35,"message":"正在渲染第12/20帧"},或者 {"status":"COMPLETED","result_url":"https://xxx.png"}。
第二步:状态字段必须包含 RUNNING / PROCESSING / COMPLETED / FAILED 四种值。别用 pending、wait 这类非标准命名,否则前端解析会直接出错。
第三步:接口必须用 HTTP 200 响应码返回所有状态。对 FAILED 状态绝对不要返回 4xx 或 5xx,否则前端轮询库可能直接终止重试,连错误信息都拿不到。
前端轮询实现:控制节奏与防雪崩
方法一:指数退避轮询(推荐)
初始间隔 2 秒,如果任务还是 RUNNING 或者请求失败,间隔翻倍(4 秒 → 8 秒 → 16 秒),最大间隔不超过 60 秒。连续 5 次状态无变化就自动终止。这样既能避免高频请求压垮后端,又能保证任务快完成时及时捕获结果。
方法二:固定间隔轮询(仅用于调试)
每 5 秒发起一次请求,简单直接。但上线后容易触发限流,尤其在并发量超过 200 时,后端 CPU 会直接飙升到 95% 以上。
切勿在前端用 setInterval 无限轮询而不设退出条件。
轮询结果处理:区分状态并终止循环
收到 status=COMPLETED 时,提取 result_url 渲染图片,并立即清除轮询定时器。收到 status=FAILED 时,显示错误 message 并停止。如果 status 长期为 RUNNING 且 progress 停滞超过 3 分钟,主动标记为“疑似卡死”,提示用户联系管理员。
轮询过程中,前端必须显示实时的 progress 百分比和 message 文本,而不是只转个圈——用户需要感知进度,不是猜谜。
一旦收到 COMPLETED 或 FAILED,必须调用 clearInterval() 或 AbortController.abort() 彻底终止所有待发请求。