当前位置:首页 > 文章列表 > 文章 > 前端 > Web TransformStream 怎样体现背压

Web TransformStream 怎样体现背压

来源:17golang原创 2026-11-15 00:56:25 0浏览 收藏

Web TransformStream 的背压,体现在写入方不能只顾着调用 writer.write(),而要让写入 Promise 和下游队列共同决定下一次写入节奏。数据从 writable 端进入转换器,transform(chunk, controller) 通过 controller.enqueue() 把结果放到 readable 端;当读取方变慢,队列的期望容量下降,继续写入就会逐渐受到等待约束。

可以把它记成一句话:enqueue() 把转换结果交给 readable 队列,writer.ready 则让生产者等待 writable 侧恢复可接受状态。TransformStream 的背压是这两端队列和管道协议共同形成的反馈,不是一个需要单独打开的布尔开关。

官方资料:https://developer.mozilla.org/en-US/docs/Web/API/TransformStream

先分清 writable、transform 和 readable

TransformStream 可以看成一对相连的流:writable 是输入入口,readable 是输出出口。写入端收到一个 chunk 后,转换器负责决定输出什么;输出并不是直接返回给调用者,而是交给 readable 侧的内部队列,随后由读取方消费。

因此,标题里的“背压”至少涉及三个位置:

  • 生产者通过 WritableStreamDefaultWriter 写入,写入动作受 writable 侧队列约束。
  • 转换器在 transform() 中调用 controller.enqueue(),把结果放入 readable 侧。
  • 读取方消费得越慢,readable 侧越接近高水位,管道越需要让上游减慢。
Web TransformStream 从 WritableStream 经过 transform 和 enqueue 进入 ReadableStream 内部队列的静态关系图
图1:TransformStream 两端与 enqueue 的静态关系示意图,不是运行截图。

enqueue 体现的是输出队列压力

controller.enqueue(value) 的作用是把一个 chunk 排入关联的 readable 侧。它没有返回“还剩多少空间”的数值,所以不能把 enqueue() 当成同步的流量控制接口;它表达的是“这次转换产生了一个输出”。真正的节奏控制要结合 writable 的写入承诺、readable 的消费速度以及队列策略来理解。

例如下面的转换器把输入文本转成大写。代码中的注释只保留会影响理解的关键点:

const upperCaseStream = new TransformStream({
  transform(chunk, controller) {
    // 每个输入块只生成一个输出块,避免一次转换无界地产生数据。
    const text = String(chunk).toUpperCase();
    controller.enqueue(text);
  },
  flush() {
    // 没有尾部缓存时不补数据,让 TransformStream 按默认流程关闭 readable。
  },
});

const writer = upperCaseStream.writable.getWriter();
const reader = upperCaseStream.readable.getReader();

async function writeWithBackpressure(chunks) {
  for (const chunk of chunks) {
    // 等待 writable 侧恢复可接受状态,再提交下一块输入。
    await writer.ready;
    await writer.write(chunk);
  }
  // 关闭写入端,让 readable 侧在剩余数据消费完后结束。
  await writer.close();
}

async function readAll() {
  let result = "";
  while (true) {
    // read() 的 done 表示转换器已经关闭且队列也没有剩余数据。
    const { value, done } = await reader.read();
    if (done) return result;
    result += value;
  }
}

await Promise.all([
  writeWithBackpressure(["web ", "transform", "stream"]),
  readAll(),
]);

这个例子中的背压关键不在“每次都显式检查某个布尔值”,而在于写入方等待 writer.ready,再等待本次 writer.write() 完成。若下游变慢,写入 Promise 就不会一直以生产者的速度无条件向前推进。

writer.ready 为什么能让生产者放慢

writer.ready 是一个 Promise。当 writable 侧的期望写入量不再处于背压状态时,它才会恢复完成。把它放在循环中,生产者就有了一个自然的“闸门”:队列尚可接受时继续写,队列需要释放空间时等待。

要注意,writer.ready 不是“所有数据已经被最终消费者处理完”的承诺。它更接近“写入端可以继续推进”的信号;而 writer.write() 的完成也表示该写入已经被 writable 管道接受到相应阶段。真正的业务确认仍然取决于下游 sink 和完整管道的错误传播。

Web TransformStream 中消费者、desiredSize、WritableStream 队列和 writer.ready 的背压反馈关系图
图2:readable 侧消费压力影响 writable 侧写入节奏的关系示意图,不是运行截图。

desiredSize 应该怎样理解

在转换器内部可以读取 controller.desiredSize,它表示 readable 侧还希望补充多少队列容量。数值为 0 时,可以理解为到达了期望水位;出现负数则表示队列相对期望容量已经超出。它适合帮助你观察或决定是否继续产生额外输出,但不能替代管道本身的 Promise 语义。

例如,一个输入块可能拆成多个输出块,此时更要限制单次转换的产出:

const splitLines = new TransformStream({
  transform(chunk, controller) {
    // 只按换行拆分当前块;真实协议若允许跨块行,需要额外保存尾部缓存。
    for (const line of String(chunk).split("\n")) {
      // 队列已达到期望水位时,不再从一个输入块继续制造更多输出。
      if (controller.desiredSize !== null && controller.desiredSize 

这里的判断只说明“当前转换是否还愿意继续产出”,并不意味着可以随意丢掉剩余业务数据。生产环境中如果不能丢数据,应保存未完成部分,等待下一次输入或把拆分逻辑设计成可暂停的异步转换。

几个容易混淆的边界

现象应该怎么判断
enqueue 没有返回值它负责排入 readable 侧,不负责给生产者提供完整的等待协议。
desiredSize 变成 0 或负数说明 readable 队列接近或超过期望水位,应该减少继续产出的速度。
writer.ready 已完成表示 writable 侧可继续推进,不等于最终业务处理已经完成。
调用 writer.close()停止新的输入,剩余输出仍需由 readable 侧消费;错误仍应通过 Promise 处理。

如果 transform() 抛出异常,或显式调用 controller.error(),读写两侧会进入错误传播路径。不要在异常后继续复用同一个 writer;应让调用方捕获写入、读取或管道 Promise 的拒绝,并根据业务决定重建流还是终止任务。

把背压写成一句可执行的判断

在大多数 TransformStream 场景中,可以采用下面的简化规则:

  1. 转换器一次只为一个输入块生成有限输出。
  2. 生产循环在 writer.ready 之后再调用 writer.write()。
  3. 需要拆分或扩增数据时观察 controller.desiredSize,但不要用它掩盖丢数据。
  4. 关闭写入端后继续消费 readable,直到 read() 返回 done: true。
  5. 同时处理写入、读取和管道的异常 Promise,避免只等待其中一端。

所以,Web TransformStream 的背压可以落到一个很具体的关系:下游的 readable 消费速度改变队列压力,队列压力影响 writable 的可推进程度,生产者再通过 writer.ready 把这种压力落实成等待。理解这条关系后,enqueue()、desiredSize 和 writer.ready 就不会再被误认为是三个互不相关的 API。

相关问题

TransformStream 的 writable 和 readable 是同一个流吗?

不是。它们是同一个 TransformStream 暴露出的输入端和输出端,内部由转换算法与队列连接。

只调用 writer.write() 不等待 writer.ready 可以吗?

少量数据通常也能工作,但持续生产时会削弱背压带来的节奏控制,容易让待处理写入快速累积。

desiredSize 小于 0 就必须丢弃数据吗?

不必须。它表示队列压力超过期望值;不能丢数据时,应暂停或缓存未完成的业务片段,而不是静默跳过。

版本声明
本文转载于:17golang原创 如有侵犯,请联系study_golang@163.com删除
Web AbortController.abort 怎样保留取消原因Web AbortController.abort 怎样保留取消原因
上一篇
Web AbortController.abort 怎样保留取消原因
Web storage 事件为什么只通知其他文档
下一篇
Web storage 事件为什么只通知其他文档
查看更多
最新文章
查看更多
课程推荐
  • 前端进阶之JavaScript设计模式
    前端进阶之JavaScript设计模式
    设计模式是开发人员在软件开发过程中面临一般问题时的解决方案,代表了最佳的实践。本课程的主打内容包括JS常见设计模式以及具体应用场景,打造一站式知识长龙服务,适合有JS基础的同学学习。
    543次学习
  • GO语言核心编程课程
    GO语言核心编程课程
    本课程采用真实案例,全面具体可落地,从理论到实践,一步一步将GO核心编程技术、编程思想、底层实现融会贯通,使学习者贴近时代脉搏,做IT互联网时代的弄潮儿。
    516次学习
  • 简单聊聊mysql8与网络通信
    简单聊聊mysql8与网络通信
    如有问题加微信:Le-studyg;在课程中,我们将首先介绍MySQL8的新特性,包括性能优化、安全增强、新数据类型等,帮助学生快速熟悉MySQL8的最新功能。接着,我们将深入解析MySQL的网络通信机制,包括协议、连接管理、数据传输等,让
    500次学习
  • JavaScript正则表达式基础与实战
    JavaScript正则表达式基础与实战
    在任何一门编程语言中,正则表达式,都是一项重要的知识,它提供了高效的字符串匹配与捕获机制,可以极大的简化程序设计。
    487次学习
  • 从零制作响应式网站—Grid布局
    从零制作响应式网站—Grid布局
    本系列教程将展示从零制作一个假想的网络科技公司官网,分为导航,轮播,关于我们,成功案例,服务流程,团队介绍,数据部分,公司动态,底部信息等内容区块。网站整体采用CSSGrid布局,支持响应式,有流畅过渡和展现动画。
    485次学习
查看更多
AI推荐
  • PubMedQA数据集详解:生物医学问答基准、功能与应用指南
    PubMedQA
    深入了解PubMedQA生物医学问答数据集,涵盖其核心功能、使用方法及在临床决策、药物研发等场景的应用,助力提升NLP模型性能。
    1029次使用
  • H2O EvalGPT:开源LLM大模型评估与排行榜工具
    H2O EvalGPT
    H2O EvalGPT是H2O.ai推出的开源LLM评估平台,提供详细的大模型性能排行榜、行业特定基准测试及A/B测试功能,助您快速选择最适合项目的高性能大语言模型。
    1030次使用
  • LMArena是什么?伯克利AI模型评估平台使用指南与功能解析
    LMArena
    LMArena是加州大学伯克利分校推出的AI模型匿名评测平台。通过盲测投票机制,用户可对比不同大模型回答并生成实时排行榜,助力开发者优化模型及用户选择最佳AI工具。
    1173次使用
  • 斯坦福HELM:大语言模型Holistic Evaluation整体评估框架详解
    HELM
    深入了解斯坦福推出的HELM(Holistic Evaluation of Language Models)大模型评测体系。本文解析其核心功能、安装配置步骤及应用场景,涵盖准确性、公平性、鲁棒性等多维度指标,助力开发者全面优化语言模型性能。
    1134次使用
  • MMBench详解:多模态大模型基准测试、功能特点与使用指南
    MMBench
    MMBench是由上海人工智能实验室等机构联合推出的多模态基准测试平台,提供细粒度能力评估、大规模数据集及VLMEvalKit工具。本文详细介绍其核心功能、安装使用方法及应用场景,助力开发者全面评估多模态模型性能。
    976次使用
微信登录更方便
  • 密码登录
  • 注册账号
登录即同意 用户协议 和 隐私政策
返回登录
  • 重置密码