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
疑问
- 为何服务端动作
getData1和getData2是串行执行而非并行? - 如何修改配置确保它们按预期并行运行?
额外上下文
- 使用Next.js 14,结合服务端动作和SWR
- 两个动作都用
setTimeout模拟5秒延迟
解答
1. 串行执行的原因
Next.js对客户端调用的服务端动作默认启用请求排队机制,核心目的是避免并发请求导致的会话状态不一致问题——尤其是在使用会话Cookie时,Next.js会确保同一会话的请求按顺序处理,防止上下文冲突。
虽然浏览器对同一域名的并发请求有数量限制,但这里的核心原因是Next.js服务端动作的内置排队逻辑,而非浏览器限制。
2. 实现并行执行的方案
方案一:用fetch直接调用API路由(推荐)
服务端动作本质是通过API路由暴露的,直接用fetch发起请求可绕过默认排队机制:
- 创建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 }); }
- 修改客户端组件的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
相关产品推荐
相关产品推荐

