You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Next.js 14中SWR调用Server Actions串行而非并行的原因及解决方法

问题描述

我在Next.js 14的客户端React组件中尝试用SWR并行获取数据,服务端动作定义如下:

// server-side actions
export async function getData1(): Promise<string> {
    console.log("getData1 start", new Date().toString());
    await new Promise(resolve => setTimeout(resolve, 5000)); // 5秒延迟
    console.log("getData1 end", new Date().toString());
    return "success";
}

export async function getData2(): Promise<string> {
    console.log("getData2 start", new Date().toString());
    await new Promise(resolve => setTimeout(resolve, 5000)); // 5秒延迟
    console.log("getData2 end", new Date().toString());
    return "success";
}

客户端组件代码:

"use client";

import React from 'react';
import { getData1, getData2 } from '@/bsky/actions';
import useSWR from 'swr';

const fetch1 = async () => {
    console.log("getData1 cs start", new Date().toString());
    await getData1();
    console.log("getData1 cs end", new Date().toString());
    return "success";
};

const fetch2 = async () => {
    console.log("getData2 cs start", new Date().toString());
    await getData2();
    console.log("getData2 cs end", new Date().toString());
    return "success";
};

const Test: React.FC = () => {
  const { data: data1 } = useSWR("fetch1", fetch1);
  const { data: data2 } = useSWR("fetch2", fetch2);

  return (
    <div className="min-h-screen p-6 max-w-8xl mx-auto">
        <div className='flex flex-col space-y-6'>
            {data1 && <div>Got data 1</div>}
            {data2 && <div>Got data 2</div>}
        </div>
    </div>
  );
};

export default Test;

预期行为

两个服务端动作getData1和getData2应并行执行,因为在客户端组件中同时发起请求。

实际行为

fetch1和fetch2同时启动,但getData2需等待getData1执行完成后才开始。日志输出顺序:

client: getData1 cs start Mon Jan 27 2025 20:01:11 GMT+0100
client: getData2 cs start Mon Jan 27 2025 20:01:11 GMT+0100

server: getData1 start Mon Jan 27 2025 20:01:12 GMT+0100
server: getData1 end Mon Jan 27 2025 20:01:17 GMT+0100

client: getData1 cs end Mon Jan 27 2025 20:01:17 GMT+0100

server: getData2 start Mon Jan 27 2025 20:01:17 GMT+0100
server: getData2 end Mon Jan 27 2025 20:01:22 GMT+0100

client: getData2 cs end Mon Jan 27 2025 20:01:22 GMT+0100

疑问

  1. 为何服务端动作getData1和getData2是串行执行而非并行?
  2. 如何修改配置确保它们按预期并行运行?

额外上下文

  • 使用Next.js 14,结合服务端动作和SWR
  • 两个动作都用setTimeout模拟5秒延迟

解答

1. 串行执行的原因

Next.js对客户端调用的服务端动作默认启用请求排队机制,核心目的是避免并发请求导致的会话状态不一致问题——尤其是在使用会话Cookie时,Next.js会确保同一会话的请求按顺序处理,防止上下文冲突。

虽然浏览器对同一域名的并发请求有数量限制,但这里的核心原因是Next.js服务端动作的内置排队逻辑,而非浏览器限制。

2. 实现并行执行的方案

方案一:用fetch直接调用API路由(推荐)

服务端动作本质是通过API路由暴露的,直接用fetch发起请求可绕过默认排队机制:

  1. 创建API路由文件app/api/data/route.ts:
import { getData1, getData2 } from '@/bsky/actions';
import { NextResponse } from 'next/server';

export async function POST(request: Request) {
  const { type } = await request.json();
  if (type === 'data1') {
    const result = await getData1();
    return NextResponse.json({ data: result });
  } else if (type === 'data2') {
    const result = await getData2();
    return NextResponse.json({ data: result });
  }
  return NextResponse.json({ error: 'Invalid type' }, { status: 400 });
}
  1. 修改客户端组件的fetch逻辑:
"use client";

import React from 'react';
import useSWR from 'swr';

const fetch1 = async () => {
    console.log("getData1 cs start", new Date().toString());
    const res = await fetch('/api/data', {
      method: 'POST',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify({ type: 'data1' })
    });
    const data = await res.json();
    console.log("getData1 cs end", new Date().toString());
    return data.data;
};

const fetch2 = async () => {
    console.log("getData2 cs start", new Date().toString());
    const res = await fetch('/api/data', {
      method: 'POST',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify({ type: 'data2' })
    });
    const data = await res.json();
    console.log("getData2 cs end", new Date().toString());
    return data.data;
};

const Test: React.FC = () => {
  const { data: data1 } = useSWR("fetch1", fetch1);
  const { data: data2 } = useSWR("fetch2", fetch2);

  return (
    <div className="min-h-screen p-6 max-w-8xl mx-auto">
        <div className='flex flex-col space-y-6'>
            {data1 && <div>Got data 1</div>}
            {data2 && <div>Got data 2</div>}
        </div>
    </div>
  );
};

export default Test;

方案二:在服务端内部并行执行

创建一个新的服务端动作,在服务端内部并行调用两个函数,再一次性返回结果:

// server-side actions
export async function getAllData(): Promise<{data1: string, data2: string}> {
  const [res1, res2] = await Promise.all([getData1(), getData2()]);
  return { data1: res1, data2: res2 };
}

客户端只需调用一次该动作:

"use client";

import React from 'react';
import { getAllData } from '@/bsky/actions';
import useSWR from 'swr';

const fetchAll = async () => {
    console.log("fetchAll cs start", new Date().toString());
    const data = await getAllData();
    console.log("fetchAll cs end", new Date().toString());
    return data;
};

const Test: React.FC = () => {
  const { data } = useSWR("fetchAll", fetchAll);

  return (
    <div className="min-h-screen p-6 max-w-8xl mx-auto">
        <div className='flex flex-col space-y-6'>
            {data?.data1 && <div>Got data 1</div>}
            {data?.data2 && <div>Got data 2</div>}
        </div>
    </div>
  );
};

export default Test;

此方案减少客户端请求数,总耗时约5秒,适合需同时获取两组数据的场景。

方案三:禁用实验性请求排队(不推荐)

在next.config.js中启用实验性配置关闭排队,但可能引发会话状态不一致风险:

/** @type {import('next').NextConfig} */
const nextConfig = {
  experimental: {
    allowConcurrentOutput: true,
  },
};

module.exports = nextConfig;

内容的提问来源于stack exchange,提问作者smaica

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.14 18:57:34