Skip to content

Node.js 26 Streams 重構:ReadableStream 與 Web Streams 的統一

Node.js 的 Streams 一直是處理大規模 I/O 的核心能力,但 Node.js Streams 和 Web Streams 兩套 API 的並存給開發者帶來了選擇困難。Node.js 26 通過底層重構,在保持向後兼容的同時,讓兩套 API 的互操作變得更加順暢。

雙 Streams 體系的現狀

在 Node.js 26 之前,Streams 存在兩個平行的體系:

javascript
// Node.js Streams(傳統)
const { Readable, Writable, Transform } = require('node:stream')
const fs = require('node:fs')

const readStream = fs.createReadStream('file.txt')
const writeStream = fs.createWriteStream('output.txt')
readStream.pipe(transformStream).pipe(writeStream)

// Web Streams(現代標準)
const response = await fetch('https://api.example.com/data')
const reader = response.body.getReader()
const writable = writableStream.getWriter()

while (true) {
  const { done, value } = await reader.read()
  if (done) break
  await writable.write(value)
}

Node.js 26 的統一策略

Node.js 26 採用了「漸進統一」的策略,在底層實現了兩套 API 的無縫互操作:

ReadableStream 增強

javascript
import { ReadableStream } from 'node:stream/web'
import { createReadStream } from 'node:fs'

// Node.js 26:直接從 fs.createReadStream 建立 Web ReadableStream
const nodeStream = createReadStream('large-file.txt')
const webStream = ReadableStream.from(nodeStream)

// Web ReadableStream 可以直接用於 fetch
const response = new Response(webStream, {
  headers: { 'Content-Type': 'text/plain' }
})

TransformStream 互操作

javascript
import { TransformStream } from 'node:stream/web'

const upperCaseTransform = new TransformStream({
  transform(chunk, controller) {
    const text = new TextDecoder().decode(chunk)
    controller.enqueue(new TextEncoder().encode(text.toUpperCase()))
  }
})

import { pipeline } from 'node:stream/promises'
import { createReadStream, createWriteStream } from 'node:fs'

await pipeline(
  createReadStream('input.txt'),
  upperCaseTransform,  // Web TransformStream 直接使用
  createWriteStream('output.txt')
)

異步迭代器:現代 Streams 的最佳實踐

javascript
import { createReadStream } from 'node:fs'

async function* processLines(readable) {
  let buffer = ''
  
  for await (const chunk of readable) {
    buffer += chunk
    const lines = buffer.split('\n')
    buffer = lines.pop() || ''
    
    for (const line of lines) {
      if (line.trim()) {
        yield JSON.parse(line)
      }
    }
  }
}

背壓處理的簡化

javascript
import { Writable } from 'node:stream'

// Node.js 26:自動背壓處理
const writable = new Writable({
  write(chunk, encoding, callback) {
    setTimeout(() => {
      console.log('Processed:', chunk.toString())
      callback()  // callback 完成後才會繼續寫入下一個 chunk
    }, 100)
  },
  highWaterMark: 16 * 1024  // 16KB
})

錯誤處理最佳實踐

javascript
import { pipeline } from 'node:stream/promises'
import { createReadStream, createWriteStream } from 'node:fs'

async function processFile(inputPath, outputPath) {
  try {
    await pipeline(
      createReadStream(inputPath),
      transformStream,
      createWriteStream(outputPath)
    )
    console.log('文件處理成功')
  } catch (err) {
    if (err.code === 'ENOENT') {
      console.error('找不到輸入文件')
    } else if (err.code === 'EACCES') {
      console.error('權限不足')
    } else {
      console.error('Stream 處理失敗:', err.message)
    }
    throw err
  }
}

總結

Node.js 26 的 Streams 重構讓 Node.js Streams 和 Web Streams 兩套 API 實現了真正的互操作。強化的異步迭代器支持、簡化的背壓處理、以及與 Fetch API 的深度集成,讓 Streams 編程變得更加直觀和高效。

MIT Licensed