技术分享

告别转圈等待:如何让用户无感刷新背后的海量任务?

作者头像 人称外号大脸猫
91 阅读
告别转圈等待:如何让用户无感刷新背后的海量任务?

你是否遇到过这种情况:点击页面的“分配”按钮后,系统立即提示“操作成功”,但列表数据却迟迟没有更新?看着那个转圈圈的加载动画,用户心里开始嘀咕:“到底成功了没有?”

这背后其实是异步任务处理的典型场景。今天,我们就来深入聊聊这个问题背后的技术考量与解决方案。

为什么会出现这种情况?

现代Web应用通常采用异步任务架构。当用户触发一个耗时操作时,系统会将其放入任务队列立即返回响应,避免用户长时间等待。

// 这就是我们常用的处理方式
SpecifyAllocateCustomersJob::dispatch($params['user_id'], $params['customer_ids']);
return $this->responseSuccess(); // 立即返回成功

但这种做法带来了新的体验问题:用户不知道任务何时真正完成,数据何时能够更新。

两种实用的解决方案

方案一:状态轮询 - 简单可靠的选择

轮询是最容易实现的方案。前端定期询问后端:“任务完成了吗?”

后端需要做这些调整:

// 分发任务时返回任务ID
$job = SpecifyAllocateCustomersJob::dispatch($userId, $customerIds);
return response()->json([
    'success' => true,
    'job_id' => $job->getJobId()
]);

// 提供状态查询接口
public function getJobStatus($jobId)
{
    $status = Redis::get("job_status:{$jobId}");
    return response()->json(['status' => $status]);
}

前端实现要点:

// 调用任务后启动轮询
const checkStatus = async (jobId) => {
  const res = await fetch(`/api/jobs/${jobId}/status`);
  const data = await res.json();
  
  if (data.status === 'completed') {
    // 刷新列表数据
    loadUserList(); 
    // 给用户明确反馈
    showNotification('分配完成');
  } else if (data.status === 'failed') {
    // 处理失败情况
    showError('分配失败,请重试');
  } else {
    // 2秒后继续检查
    setTimeout(() => checkStatus(jobId), 2000);
  }
};

这种方法虽然简单,但需要考虑轮询频率:太频繁会增加服务器压力,太稀疏会影响用户体验。

方案二:WebSocket实时推送 - 更优的体验

如果你追求更好的用户体验,WebSocket是更好的选择。

后端实现事件推送:

// 定义任务完成事件
class AllocationCompleted implements ShouldBroadcast
{
    public $userId;
    public $result;
    
    public function broadcastOn()
    {
        // 推送到指定用户的私有频道
        return new PrivateChannel("allocations.{$this->userId}");
    }
}

// 任务处理完成后触发事件
class AllocationJob implements ShouldQueue
{
    public function handle()
    {
        // 处理分配逻辑...
        event(new AllocationCompleted($userId, $result));
    }
}

前端监听实时事件:

// 建立WebSocket连接
Echo.private(`allocations.${currentUserId}`)
    .listen('AllocationCompleted', (e) => {
        // 收到通知后立即更新界面
        updateCustomerList(e.result);
        showNotification('客户分配完成!');
    });

这种方案的优点是实时性好,服务器压力小,但需要额外的WebSocket服务支持。

如何选择适合的方案?

根据你的项目需求做出选择:

  • 小型项目:选择轮询方案,实现简单,无需额外基础设施
  • 中大型项目:建议使用WebSocket,提供更好的用户体验
  • 混合方案:可以先尝试WebSocket,失败后降级为轮询

提升用户体验的细节处理

无论采用哪种方案,这些细节都能显著提升体验:

  1. 提供进度反馈:显示“处理中,请稍候...”的提示
  2. 设置超时时间:避免网络问题导致无限等待
  3. 错误处理:任务失败时提供重试选项
  4. 状态持久化:页面刷新后仍能查看任务状态

总结

异步任务处理是现代Web应用的必备特性,但处理好用户体验同样重要。通过合适的方案选择和技术实现,我们既能保证系统性能,又能提供流畅的用户体验。