网络请求与实时通道

系列文章目录

《JavaScript 基础与进阶笔记》(前期偏基础巩固与常见面试点,后续进入闭包、异步、工程化等进阶主题)


文章目录


前言

第 25 篇讲了缓存、跨域与鉴权;本篇聚焦 怎么发请求、怎么收实时数据XHRFetch 的取舍,以及 轮询 / SSE / WebSocket 如何选型。面试常问:Fetch 和 XHR 区别、SSE 和 WebSocket 区别、wss、断线如何重连。


一、XHR 与 Fetch

1.1 对比

XHR(XMLHttpRequest) Fetch API
API 风格 事件回调 / 需封装 Promise 原生返回 Promise
使用场景 老项目、部分库底层 现代首选
请求取消 xhr.abort() AbortController
进度 onprogress 支持较好 ReadableStream
Cookie withCredentials credentials: 'include'
响应类型 responseType(json/blob...) .json() / .text()
javascript 复制代码
/* XHR --- 了解即可,新项目少用 */
const xhr = new XMLHttpRequest();
xhr.open("GET", "/api/user");
xhr.withCredentials = true;
xhr.onload = () => console.log(JSON.parse(xhr.responseText));
xhr.send();

/* Fetch --- 推荐 */
const res = await fetch("/api/user", { credentials: "include" });
if (!res.ok) throw new Error(res.status);
const data = await res.json();

1.2 Fetch 注意点

  1. 只有网络错误才 reject ;HTTP 4xx/5xx 仍 resolve ,需判断 res.okstatus 200~299)。
  2. 默认不带 Cookie ;跨域要带 Cookie 用 credentials: 'include'(配合第 25 篇 CORS)。
  3. 不会自动发 JSONPOST 发对象需自己 JSON.stringify 并设 Content-Type
javascript 复制代码
const res = await fetch("/api/save", {
  method: "POST",
  headers: { "Content-Type": "application/json" },
  credentials: "include",
  body: JSON.stringify({ name: "Tom" }),
});

if (!res.ok) throw new Error(`HTTP ${res.status}`);

1.3 取消请求:AbortController

javascript 复制代码
const controller = new AbortController();

fetch("/api/slow", { signal: controller.signal })
  .then((r) => r.json())
  .catch((e) => {
    if (e.name === "AbortError") console.log("已取消");
  });

/* 组件卸载或用户取消时 */
controller.abort();

二、实时数据:四种方式对比

方式 方向 连接 典型场景
短轮询 客户端反复请求 多次 HTTP 简单通知、兼容性要求高
长轮询 服务端 hold 住直到有数据 多次 HTTP(单次较长) 旧 IM、降级方案
SSE 服务端 → 客户端 单向 长连接 HTTP 股票行情、日志流、AI 流式输出
WebSocket 双向 长连接,协议升级 聊天、协作、游戏、弹幕

口诀 :只要 服务端推、客户端不常发 → 优先 SSE ;要 双向、低延迟WebSocket ;临时方案可用 轮询


三、SSE(Server-Sent Events)

基于 HTTP 长连接 ,服务端持续推送 text/event-stream 格式数据;浏览器用 EventSource

javascript 复制代码
const es = new EventSource("/api/stream", { withCredentials: true });

es.onmessage = (e) => {
  console.log("默认事件:", e.data);
};

es.addEventListener("price", (e) => {
  const { symbol, value } = JSON.parse(e.data);
  console.log(symbol, value);
});

es.onerror = () => {
  /* 断线后浏览器会自动重连(有间隔) */
  console.warn("SSE 连接异常");
};

/* 关闭 */
// es.close();

特点

  • 单向:客户端发消息仍要走普通 HTTP
  • 自动重连(浏览器内置)
  • 仅文本;二进制需 Base64 或换 WebSocket
  • HTTP/2 多路复用 下表现良好

四、WebSocket

通过 HTTP Upgrade 升级为 ws/wss 协议,全双工、帧开销小。

javascript 复制代码
function connect(url) {
  const ws = new WebSocket(url); /* 生产用 wss:// */

  ws.onopen = () => {
    ws.send(JSON.stringify({ type: "ping" }));
  };

  ws.onmessage = ({ data }) => {
    console.log("收到:", JSON.parse(data));
  };

  ws.onclose = (e) => {
    console.log("关闭", e.code, e.reason);
    /* 简单重连 --- 生产建议指数退避 + 上限 */
    setTimeout(() => connect(url), 3000);
  };

  ws.onerror = () => console.error("WS 错误");

  return ws;
}

const ws = connect("wss://api.example.com/ws");

4.1 心跳与重连(工程要点)

问题 做法
中间代理超时断开 定时发 ping(应用层 JSON 或协议层 ping)
断线无感知 监听 close/error指数退避重连(1s、2s、4s... capped)
重复连接 重连前 close 旧实例;页面 visibility 隐藏时可暂停
安全 生产必须用 wss://(TLS)
javascript 复制代码
/* 指数退避重连示意 */
let retry = 0;
function reconnect(url) {
  const delay = Math.min(1000 * 2 ** retry, 30000);
  setTimeout(() => {
    retry++;
    connect(url);
  }, delay);
}

4.2 readyState

状态
0 CONNECTING
1 OPEN
2 CLOSING
3 CLOSED

五、SSE vs WebSocket(面试口述)

SSE WebSocket
通信 单向推送 双向
协议 普通 HTTP Upgrade
实现 简单,EventSource 稍复杂,需处理心跳重连
数据 文本事件流 文本 + 二进制
代理/CDN 友好 部分代理需额外配置
自动重连 浏览器内置 需自己实现

六、选型口诀

需求 优先
普通 REST API Fetch + axios 封装
服务端持续推送、客户端少发 SSE
聊天、对战、双向高频 WebSocket
极简、防火墙只认 HTTP 轮询 / SSE
上传进度、极老浏览器 XHR

七、易混淆点归纳

  1. Fetch 的 404 不会进 catch ,要看 res.ok
  2. SSE 不能代替 WebSocket 做双向聊天(除非另开 HTTP 上报)。
  3. WebSocket 不会自动重连;SSE 会。
  4. ws:// 明文 ,生产用 wss://
  5. 轮询 浪费连接与流量;实时性要求高时避免高频短轮询。
  6. AbortController 可取消 Fetch;XHR 用 abort()

八、思考与练习

1. Fetch 收到 HTTP 500,await fetch(url) 会抛错吗?

解析:不会 (网络层成功);需判断 !res.ok 再抛业务错误。

2. 股票行情只从服务端推价格,客户端不发消息,选 SSE 还是 WebSocket?

解析:优先 SSE (更简单);若要二进制或极低延迟双向再考虑 WebSocket

3. WebSocket 断线后为何要做指数退避?

解析:避免服务端故障时 大量客户端同时狂重连(惊群),逐步拉长间隔。

4. credentials: 'include' 在 Fetch 里做什么?与 CORS 有何关系?

解析:跨域请求 携带 Cookie ;服务端须 Access-Control-Allow-Credentials: true 且 Origin 不能为 *

5. EventSource 与 WebSocket 谁自带自动重连?

解析:EventSource(SSE) 自带;WebSocket 需自行实现。


总结

  • Fetch 替代 XHR 为主流;注意 res.okcredentialsAbortController
  • 实时推送 :单向 SSE ;双向 WebSocket(wss + 心跳 + 重连)
  • 轮询 作兜底,非首选。

下一篇讲 Service Worker 与 PWA :离线缓存、fetch 拦截(系列第 27 篇)。

相关推荐
kTR2hD1qb2 小时前
从 Responses API 到 Chat Completions:一个模型网关的设计复盘
linux·前端
德迅云安全-甲锵2 小时前
解析CDN防护核心原理:筑牢网络业务安全屏障
网络·安全
闪电悠米3 小时前
黑马点评-Redisson-01_why_redisson
java·服务器·网络·数据库·缓存·wpf
鹿鸣天涯3 小时前
网规第三版:第8章网络故障分析与处理案例
网络·软考·网络规划设计师
kyriewen3 小时前
浏览器缓存最强攻略:强缓存、协商缓存、CDN、更新策略,一篇搞定
前端·面试·浏览器
上海云盾-小余3 小时前
CN2 与 BGP 线路优劣拆解,按需选配规避延迟与攻击隐患
网络
持敬chijing3 小时前
Web渗透之SQL注入-联合查询注入-注入点数据类型判断
前端·sql·安全·web安全·网络安全·安全威胁分析
卷帘依旧4 小时前
Web3前端一面
前端