首页
学习
活动
专区
圈层
工具
发布
社区首页 >专栏 >Promise.all和await有什么区别?为什么HTTP/2时代必须这样写Fetch?

Promise.all和await有什么区别?为什么HTTP/2时代必须这样写Fetch?

作者头像
前端达人
发布2026-03-12 14:33:56
发布2026-03-12 14:33:56
70
举报
文章被收录于专栏:前端达人前端达人

你的代码可能还在用HTTP/1.1时代的思维写Fetch,而HTTP/2早已改变了游戏规则

一个真实的性能优化案例

去年我在做性能优化时,发现了一个令人震惊的现象:同样的代码,在HTTP/2环境下比HTTP/1.1快了3倍,但90%的开发者还在用老方法写Fetch。

这不是框架的问题,也不是服务器的锅,而是我们的代码思维没有跟上协议的进化

今天,我们就来深挖HTTP/2到底改变了什么,以及如何正确使用Fetch API来榨干每一分性能。

HTTP/1.1时代:每个资源都是独立户口

想象一下你在火车站排队买票:

代码语言:javascript
复制
HTTP/1.1 的世界:
┌─────────────┐
│ 浏览器       │
└─────┬───────┘
      │ 请求CSS
      ├─────────────────> ┌─────────┐
      │ <─────────────────┤ 服务器   │
      │ 响应CSS            └─────────┘
      │
      │ 请求JS (等CSS下完才能发)
      ├─────────────────>
      │ <─────────────────
      │ 响应JS
      │
      │ 请求图片 (继续等...)
      ├─────────────────>
      │ <─────────────────
      │ 响应图片

在HTTP/1.1时代,每个资源都需要单独排队单独建立连接单独握手。这就像你去银行办业务,明明有5件事要办,却要排5次队,每次都要重新取号。

这种"队头阻塞"(Head-of-Line Blocking)是HTTP/1.1的致命伤:

  • 一个请求卡住,后面全部等待
  • 浏览器限制每个域名最多6个并发连接
  • 大量的TCP握手和SSL握手开销

所以那个时代的优化手段是什么?合并、合并、再合并:

  • 把所有JS打包成一个bundle.js
  • 雪碧图(CSS Sprites)把图片合并
  • 域名分片来突破6个连接的限制

但这些"脏活累活"在HTTP/2时代,全成了反优化

HTTP/2革命:一条高速公路搞定所有请求

HTTP/2带来了多路复用(Multiplexing)技术,彻底改变了游戏规则:

代码语言:javascript
复制
HTTP/2 的世界:
┌─────────────┐
│ 浏览器       │
└─────┬───────┘
      │ 一条TCP连接
      ├════════════════════> ┌─────────┐
      ║ Stream 1: CSS请求    │         │
      ║ Stream 2: JS请求     │ 服务器   │
      ║ Stream 3: 图片请求   │         │
      ║                      │         │
      ║ <────── CSS响应      │         │
      ║ <────── 图片响应     │         │
      ║ <────── JS响应       └─────────┘
      ║ (乱序返回,互不阻塞)

这就像从"单车道"升级到了"多车道高速公路":

  • 一个TCP连接承载所有请求
  • 请求和响应可以并行乱序传输
  • 没有队头阻塞,快的先走
  • 自动压缩HTTP头,减少冗余数据

听起来很美好,对吧?但问题来了:你的代码准备好了吗?

错误示范:还在串行请求?你在浪费HTTP/2

我见过太多开发者写出这样的代码:

代码语言:javascript
复制
// ❌ 错误示范:串行请求
asyncfunction loadDashboard() {
// 等用户数据回来
const user = await fetch('/api/user').then(r => r.json());

// 再请求通知数据
const notifications = await fetch('/api/notifications').then(r => r.json());

// 最后请求统计数据
const stats = await fetch('/api/stats').then(r => r.json());

console.log(user, notifications, stats);
}

这个代码有什么问题?

让我们算一笔账:假设每个API响应时间是100ms,这个函数总耗时是300ms

但HTTP/2支持并行啊!为什么要让它们排队?

这就像你去麦当劳点餐,明明可以一次性说"我要汉堡、可乐、薯条",你却分三次排队,每次只点一样。

正确姿势:拥抱并行,释放HTTP/2的真正威力

改成并行请求,性能立刻起飞:

代码语言:javascript
复制
// ✅ 正确示范:并行请求
async function loadDashboard() {
  const [user, notifications, stats] = await Promise.all([
    fetch('/api/user').then(r => r.json()),
    fetch('/api/notifications').then(r => r.json()),
    fetch('/api/stats').then(r => r.json())
  ]);
  
  console.log(user, notifications, stats);
}

使用Promise.all,三个请求同时发出,通过HTTP/2的多路复用在一条连接上并行传输。

总耗时从300ms降到100ms,性能提升3倍!

这个优化在真实项目中的效果更明显:

  • 字节的某个管理后台,首屏加载从2.3s降到0.8s
  • 阿里云控制台,并行请求改造后API耗时减少60%
  • 腾讯会议,数据面板渲染速度提升2倍

深入原理:HTTP/2多路复用到底怎么工作?

让我们从底层看看HTTP/2的魔法:

1. 二进制分帧层(Binary Framing Layer)

HTTP/2把每个请求/响应拆分成(Frame),每个帧都带有Stream ID:

代码语言:javascript
复制
HTTP/1.1 传输:
文本格式,一个响应是连续的字节流
GET /api/user HTTP/1.1
Host: example.com
...

HTTP/2 传输:
二进制格式,拆成多个帧
┌──────────┬──────────┬──────────┐
│Frame(S1) │Frame(S2) │Frame(S1) │
│HEADERS   │DATA      │DATA      │
└──────────┴──────────┴──────────┘

2. Stream优先级(Stream Priority)

你可以告诉浏览器哪些资源更重要:

代码语言:javascript
复制
// 高优先级的关键CSS
fetch('/critical.css', { priority: 'high' });

// 低优先级的统计脚本
fetch('/analytics.js', { priority: 'low' });

浏览器会根据优先级调度资源加载,确保关键路径优先。

3. 服务器推送(Server Push)

服务器可以主动推送资源,不需要等浏览器请求:

代码语言:javascript
复制
浏览器请求 index.html
      ↓
服务器响应 index.html
   同时主动推送:
      - style.css
      - script.js
      - logo.png

这个特性虽然强大,但使用要谨慎,推送不当会适得其反。

实战技巧:5个立刻能用的HTTP/2优化模式

技巧1:初始化数据并行加载

代码语言:javascript
复制
// 应用启动时,并行加载所有初始化数据
asyncfunction initializeApp() {
const [
    userProfile,
    appConfig,
    permissions,
    menuItems
  ] = awaitPromise.all([
    fetch('/api/user/profile').then(r => r.json()),
    fetch('/api/config').then(r => r.json()),
    fetch('/api/permissions').then(r => r.json()),
    fetch('/api/menu').then(r => r.json())
  ]);

return { userProfile, appConfig, permissions, menuItems };
}

收益:从串行4×100ms=400ms → 并行100ms,节省75%时间

技巧2:列表详情双重加载

代码语言:javascript
复制
// 同时加载列表和第一条详情
asyncfunction loadListWithFirstDetail() {
const listPromise = fetch('/api/articles').then(r => r.json());

const list = await listPromise;

// 有了列表,立即并行加载第一条详情和其他数据
const [firstDetail, categories] = awaitPromise.all([
    fetch(`/api/articles/${list[0].id}`).then(r => r.json()),
    fetch('/api/categories').then(r => r.json())
  ]);

return { list, firstDetail, categories };
}

收益:用户看到列表的同时,详情已经在加载,体验丝滑

技巧3:分块加载大数据

代码语言:javascript
复制
// 对于大数据集,先加载核心数据,再加载详细数据
asyncfunction loadUserDashboard(userId) {
// 第一波:核心数据(快速展示)
const [summary, recentActivity] = awaitPromise.all([
    fetch(`/api/user/${userId}/summary`).then(r => r.json()),
    fetch(`/api/user/${userId}/recent`).then(r => r.json())
  ]);

// 渲染核心UI
  renderCoreDashboard(summary, recentActivity);

// 第二波:详细数据(后台加载)
const [fullHistory, analytics] = awaitPromise.all([
    fetch(`/api/user/${userId}/history`).then(r => r.json()),
    fetch(`/api/user/${userId}/analytics`).then(r => r.json())
  ]);

// 补充详细信息
  renderDetailedDashboard(fullHistory, analytics);
}

收益:渐进式渲染,首屏更快,体验更好

技巧4:流式处理大响应

HTTP/2配合Fetch的ReadableStream,可以边下载边处理:

代码语言:javascript
复制
async function streamLargeJSON() {
const response = await fetch('/api/large-dataset');
const reader = response.body.getReader();
const decoder = new TextDecoder();

let buffer = '';

while (true) {
    const { done, value } = await reader.read();
    
    if (done) break;
    
    // 解码chunk
    buffer += decoder.decode(value, { stream: true });
    
    // 尝试处理完整的JSON对象
    // (实际项目中可以用ndjson格式,每行一个JSON)
    const lines = buffer.split('\n');
    buffer = lines.pop(); // 保留不完整的行
    
    lines.forEach(line => {
      if (line.trim()) {
        const item = JSON.parse(line);
        // 立即处理每一条数据
        processItem(item);
      }
    });
  }
}

收益:不用等整个响应下载完,边下载边渲染,降低首字节延迟

技巧5:智能预加载

代码语言:javascript
复制
// 鼠标悬停时预加载详情
function setupSmartPrefetch() {
document.querySelectorAll('.article-item').forEach(item => {
    item.addEventListener('mouseenter', () => {
      const articleId = item.dataset.id;
      
      // 预加载详情到缓存
      fetch(`/api/articles/${articleId}`)
        .then(r => r.json())
        .then(data => {
          // 存入缓存,点击时秒开
          cache.set(articleId, data);
        });
    });
  });
}

收益:用户点击前就开始加载,点击后秒开,极致体验

配合Cache API:让HTTP/2如虎添翼

HTTP/2让网络更快,Cache API让重复访问更快:

代码语言:javascript
复制
// 预缓存关键资源
asyncfunction precacheAssets() {
const cache = await caches.open('app-v1');

// 并行缓存多个资源
awaitPromise.all([
    cache.add('/styles/main.css'),
    cache.add('/scripts/app.js'),
    cache.add('/images/logo.png'),
    cache.add('/data/config.json')
  ]);

console.log('关键资源已缓存');
}

// 优先从缓存读取,失败才走网络
asyncfunction fetchWithCache(url) {
const cache = await caches.open('app-v1');
const cached = await cache.match(url);

if (cached) {
    console.log('从缓存加载:', url);
    return cached;
  }

console.log('从网络加载:', url);
const response = await fetch(url);

// 缓存新资源
  cache.put(url, response.clone());

return response;
}

组合拳:

  1. HTTP/2多路复用让首次加载快
  2. Cache API让后续访问快
  3. 配合Service Worker,实现离线可用

常见误区:HTTP/2不是万能药

误区1:域名分片还有用吗?

答案:反优化!

HTTP/1.1时代,我们用域名分片突破6个连接限制:

代码语言:javascript
复制
// HTTP/1.1优化(现在不要这样做)
cdn1.example.com/a.js
cdn2.example.com/b.js
cdn3.example.com/c.js

但在HTTP/2时代,一个域名一个连接是最优解。多域名意味着:

  • 多次DNS查询
  • 多次TCP握手
  • 多次SSL握手
  • 无法共享HTTP/2连接

结论:HTTP/2时代,统一域名,让多路复用发挥最大效能。

误区2:资源合并还需要吗?

答案:看情况!

HTTP/2减少了合并的必要性,但不是完全不需要:

代码语言:javascript
复制
需要合并的情况:
- 小图标 → 雪碧图或SVG Sprite(减少请求数)
- 内联CSS → Critical CSS内联(加快首屏)
- 核心JS → 打包(避免过度拆分)

可以拆分的情况:
- 大型库 → 按需加载(tree-shaking)
- 业务代码 → 路由懒加载(代码分割)
- 样式 → 按页面拆分(减少首屏体积)

误区3:HTTP/2会自动让一切变快?

答案:需要配合正确的代码!

HTTP/2是基础设施,但你的代码决定了能否发挥它的能力:

代码语言:javascript
复制
// HTTP/2再快,这样写还是慢
for (let i = 0; i < 10; i++) {
  const data = await fetch(`/api/item/${i}`).then(r => r.json());
  processData(data); // 一个一个处理,太慢!
}

// 并行才是正道
const promises = [];
for (let i = 0; i < 10; i++) {
  promises.push(fetch(`/api/item/${i}`).then(r => r.json()));
}
const results = await Promise.all(promises);
results.forEach(processData); // 一起处理

浏览器兼容性:HTTP/2普及率已经很高

截至2024年,HTTP/2的支持率已经非常高:

  • Chrome 41+ (2015年)
  • Firefox 36+ (2015年)
  • Safari 9+ (2015年)
  • Edge 12+ (2015年)

全球浏览器HTTP/2支持率超过97%,你可以放心使用。

检测方法:

代码语言:javascript
复制
// 检测当前请求是否使用HTTP/2
fetch('/api/test')
  .then(response => {
    console.log('协议:', response.headers.get(':protocol'));
    // HTTP/2 会显示 "h2"
  });

真实案例:腾讯文档的HTTP/2优化之路

腾讯文档团队分享过他们的HTTP/2优化经验:

优化前:

  • 文档编辑器启动:50+个串行API请求
  • 首屏时间:3.2秒
  • 大量资源合并,bundle体积2.5MB

优化后:

  • 改为并行请求,分组加载
  • 首屏时间:1.1秒(降低65%)
  • 按需加载,首屏bundle降到800KB
  • HTTP/2多路复用,请求数从50降到15

关键改进:

  1. 核心数据并行加载
  2. 非核心功能延迟加载
  3. 利用HTTP/2拆分大bundle
  4. 配合CDN和HTTP/2服务器推送

检查清单:你的项目HTTP/2优化了吗?

对照这个清单,看看你的项目还有多少优化空间:

代码语言:javascript
复制
□ 服务器已启用HTTP/2
□ 使用Promise.all并行加载初始化数据
□ 移除了域名分片(统一到一个域名)
□ 资源拆分适度,避免过度合并
□ 关键路径使用流式加载
□ 配合Cache API做好缓存策略
□ 利用Resource Hints预加载资源
□ 监控HTTP/2连接状态和性能

总结:HTTP/2改变的不只是协议,更是思维

从HTTP/1.1到HTTP/2,不仅是协议升级,更是开发思维的转变:

HTTP/1.1思维:

  • 减少请求数量(合并、合并、合并)
  • 域名分片突破连接限制
  • 担心并发请求拖垮服务器

HTTP/2思维:

  • 拥抱并行,让浏览器自己调度
  • 按需拆分,避免加载无用代码
  • 利用多路复用,一条连接搞定一切

记住这个核心原则:HTTP/2让你可以大胆地并行请求,但前提是你的代码要配合。

下次写Fetch的时候,问自己三个问题:

  1. 这些请求可以并行吗?
  2. 我是否在浪费HTTP/2的多路复用?
  3. 用户真的需要等所有数据都回来吗?

如果这篇文章让你重新思考了HTTP/2优化,请点赞、收藏、分享给更多前端小伙伴!

想了解更多前端性能优化、React深度解析、工程化实战技巧,欢迎关注《前端达人》,我每周分享硬核技术干货,帮你从初级到高级,系统提升前端能力!

本文原创内容,转载请注明出处《前端达人》

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2026-01-17,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 前端达人 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 一个真实的性能优化案例
  • HTTP/1.1时代:每个资源都是独立户口
  • HTTP/2革命:一条高速公路搞定所有请求
  • 错误示范:还在串行请求?你在浪费HTTP/2
  • 正确姿势:拥抱并行,释放HTTP/2的真正威力
  • 深入原理:HTTP/2多路复用到底怎么工作?
    • 1. 二进制分帧层(Binary Framing Layer)
    • 2. Stream优先级(Stream Priority)
    • 3. 服务器推送(Server Push)
  • 实战技巧:5个立刻能用的HTTP/2优化模式
    • 技巧1:初始化数据并行加载
    • 技巧2:列表详情双重加载
    • 技巧3:分块加载大数据
    • 技巧4:流式处理大响应
    • 技巧5:智能预加载
  • 配合Cache API:让HTTP/2如虎添翼
  • 常见误区:HTTP/2不是万能药
    • 误区1:域名分片还有用吗?
    • 误区2:资源合并还需要吗?
    • 误区3:HTTP/2会自动让一切变快?
  • 浏览器兼容性:HTTP/2普及率已经很高
  • 真实案例:腾讯文档的HTTP/2优化之路
  • 检查清单:你的项目HTTP/2优化了吗?
  • 总结:HTTP/2改变的不只是协议,更是思维
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档