不少开发者存在一个常见误解:误认为 await 能够实现并行执行,但实际默认是串行的。真正引发页面数据渲染“塌陷”的元凶,通常是 Promise.all() 的“一损俱损”机制——只要其中一个请求失败,整个 Promise 就会 reject,后续逻辑全部中断,导致页面无法获取任何数据。

明确区分:串行 await 与并行 Promise 的不同
这正是问题的根源。例如下面的写法,一旦某个请求失败,后续所有请求都会停止:
await fetch('/user')await fetch('/orders')await fetch('/profile')
再看下面这种看似并行的写法,默认却会导致整体失败:
const [user, orders, profile] = await Promise.all([fetch(...), fetch(...), fetch(...)])
只要任何一个 fetch 遇到网络错误、404 或 500,Promise.all 就会立刻 reject,导致整个解构赋值失败,所有变量变为 undefined,视图渲染自然崩溃。
使用 Promise.allSettled() 兜底所有结果
它会等待所有 Promise 执行完毕(无论成功或失败),然后返回一个结构固定的数组。每个元素包含 status: 'fulfilled' | 'rejected' 以及对应的 value 或 reason。
- 尤其适用于“尽力而为”的场景:例如用户资料加载失败,但订单和设置信息仍然需要正常展示
- 返回值可以统一处理,流程不会中断
- 实际案例:即使第三个请求失败,前两个请求获取的数据依然可用
为每个 Promise 单独添加 catch,转换为“安全值”
如果仍然希望使用 Promise.all(例如需要所有请求都成功才能继续执行),又不想因单个失败而崩溃,可以在每个 Promise 后链式调用 .catch(),捕获异常并返回一个默认值:
fetch('/user').then(r => r.json()).catch(() => null)fetch('/orders').then(r => r.json()).catch(() => [])fetch('/profile').then(r => r.json()).catch(() => ({ theme: 'light' }))
这样一来,Promise.all 始终能获取三个“正常值”,不会 reject,解构操作安全可靠,页面渲染稳如磐石。
警惕 .json() 本身也可能抛出异常
需要特别警惕的是:fetch 请求成功(状态码 200)并不意味着响应体一定是合法的 JSON。直接使用 await res.json() 可能因格式错误而抛出 SyntaxError——这是一个容易被忽视的陷阱。
- 正确做法:在
.json()后也添加.catch,或将其包裹在 try/catch 块中 - 举个安全的写法:
fetch(...).then(r => r.json().catch(e => { console.warn('JSON 解析失败', e); return null; }))
处理并行请求时,真正的稳健并非依赖某个方法的强大,而是要对所有可能出现错误的环节预留退路。使用 Promise.allSettled 或逐个添加 catch,本质上都是在执行层面为异常处理留出余地——这才是保障数据渲染不“塌陷”的核心思路。
