网络请求与实时通道

系列文章目录

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


文章目录


前言

第 25 篇讲了缓存、跨域与鉴权;本篇聚焦 怎么发请求、怎么收实时数据 :XHR 与 Fetch 的取舍,以及 轮询 / 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.ok (status 200~299)。
  2. 默认不带 Cookie ;跨域要带 Cookie 用 credentials: 'include'(配合第 25 篇 CORS)。
  3. 不会自动发 JSON ;POST 发对象需自己 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.ok 、credentials 、AbortController。
  • 实时推送 :单向 SSE ;双向 WebSocket(wss + 心跳 + 重连)。
  • 轮询 作兜底,非首选。

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

相关推荐
爱喝水的小周3 分钟前
实验十 Vue‑Router 王者荣耀战绩查询 APP 开发实践
前端·javascript·vue.js
yexianglunbai3 分钟前
Vue 3 全面详解:从核心特性到工程化实践
前端·javascript·vue.js
传人once5 分钟前
选择文件按钮样式如何美化
前端·html·css3·jquery·html5
马六六i6 分钟前
市面上专业的IP驱动产业新场景新工具哪家好
网络·人工智能·python·tcp/ip
星恒讯工业路由器19 分钟前
工信部调整超宽带设备频段:7-9GHz为5G/6G腾频谱,工业UWB设备面临合规升级
网络·物联网·5g·无线通信·5g专网·超宽带uwb·频谱管理
俊哥大数据20 分钟前
Flink 2.3.0 从理论到实践 —— 第 2 章 Flink 运行时架构
网络·架构·flink
parade岁月20 分钟前
告别 Deno 时代?运行时进入一年倒计时,Deno 团队并入 Cloudflare
前端
xiaoye-duck22 分钟前
《Linux 网络编程》从 0 手写 Reactor 反应堆(上):拆解 Reactor 架构 —— 连接抽象、Poller 封装与事件派发核心
linux·网络
Lucky_Turtle24 分钟前
【HTML】前端页面禁止缩放
前端·html
砚凝霜25 分钟前
【软考信息安全】第十二章 网络安全审计概述与分类
网络·安全·web安全