Bun

Bun v1.1.23


Ashcon Partovi · 2024 年 8 月 14 日

Bun v1.1.23 版本在此!此版本修正了 27 個錯誤(解決了 128 個 👍)。支援 TextEncoderStreamTextDecoderStreamconsole.log(string) 速度提升 50%、支援 Float16Array、更佳的記憶體不足處理、Windows 上的 Node.js <> Bun IPC、在 console.log() 中截斷大型陣列,以及更多錯誤修正和 Node.js 相容性改進。

我們正在舊金山招募系統工程師,一同打造 JavaScript 的未來!

先前的版本

  • v1.1.22 修正了 72 個錯誤(解決了 63 個 👍)。fetch() 解壓縮速度提升 30%、新的 --fetch-preconnect 標誌、改進的 Remix 支援、Bun 在 Linux 上縮小了 4 MB、捆綁套件時排除依賴項、許多捆綁器修正和 node 相容性改進。
  • v1.1.0 Bundows。Windows 支援來了!

安裝 Bun

curl
npm
powershell
scoop
brew
docker
curl
curl -fsSL https://bun.dev.org.tw/install | bash
npm
npm install -g bun
powershell
powershell -c "irm bun.sh/install.ps1|iex"
scoop
scoop install bun
brew
brew tap oven-sh/bun
brew install bun
docker
docker pull oven/bun
docker run --rm --init --ulimit memlock=-1:-1 oven/bun

升級 Bun

bun upgrade

新功能

TextEncoderStream & TextDecoderStream

我們已實作 TextEncoderStreamTextDecoderStream Web API。這些 API 是 TextEncoderTextDecoder 的串流等效物。

您可以使用 TextDecoderStream 將位元組串流解碼為 UTF-8 字串串流。

const response = await fetch("https://example.com");
const body = response.body.pipeThrough(new TextDecoderStream());

for await (const chunk of body) {
  console.log(chunk); // typeof chunk === "string"
}

您可以使用 TextEncoderStream 將 UTF-8 字串串流編碼為位元組串流。

const stream = new ReadableStream({
  start(controller) {
    controller.enqueue("Hello, world!");
    controller.close();
  },
});
const body = stream.pipeThrough(new TextEncoderStream());

for await (const chunk of body) {
  console.log(chunk); // chunk instanceof Uint8Array
}

Bun 中的 TextEncoderStream 速度相對較快。

為什麼需要 TextEncoderStream?

TextEncoderStream API 用於熱門套件中,例如 Next.js 的 App Router 的中介軟體。

由於 Bun 中大多數原生實作的串流都支援文字和二進制資料,因此您很少需要在 Bun 中使用 TextEncoderStream。事實上,在 Bun 中使用 TextEncoderStream 比不使用它更慢,因為它會為串流增加顯著的額外負擔。因此,除非您依賴的程式庫已經在使用它,否則請勿使用此 API。

TextDecoder 中的 stream 選項

此版本也新增了對 TextDecoderstream 選項的支援。這會告知解碼器區塊是較大串流的一部分,如果區塊不是完整的 UTF-8 程式碼點,則不應拋出錯誤。

const decoder = new TextDecoder("utf-8");
const first = decoder.decode(new Uint8Array([226, 153]), { stream: true });
const second = decoder.decode(new Uint8Array([165]), { stream: true });

console.log(first); // ""
console.log(second); // "♥"

console.log(string) 速度提升 50%

在此版本的 Bun 中,我們將 console.log(string) 的速度提高了 50%。

先前,Bun 對於使用 console.log() 列印單個字串具有快速路徑。理論上,這表示列印字串只需要 1 個系統呼叫。但是,情況並非如此,因為 console.log() 需要列印換行字元並重設 ANSI 逸出碼。我們透過移除這個不那麼快速的路徑並像其他情況一樣緩衝寫入來使其更快。

感謝 @billywhizz@nektro 使其速度更快!

console.log() 中截斷大型陣列

當您在 Bun 中 console.log(largeArray) 一個大型陣列時,Bun 現在不會逐個元素列印整個陣列,而是在列印總共 100 個元素後停止,並列印省略符號和計數 (... 還有 1,234 個項目) 以指示還有更多元素。

感謝 @nektro 致力於此功能!

Float16Array

在此版本的 Bun 中,支援新加入的 Float16Array API。這是 WebKit 中實作的 stage 3 TC39 提案。

const float16 = new Float16Array(3);
const float32 = new Float32Array(3);

for (let i = 0; i < 3; i++) {
  float16[i] = i + 0.123;
  float32[i] = i + 0.123;
}

console.log(float16); // Float16Array(3) [ 0, 1.123046875, 2.123046875 ]
console.log(float32); // Float32Array(3) [ 0, 1.1230000257492065, 2.122999906539917 ]

感謝 WebKit 團隊實作此功能!

更佳的記憶體不足錯誤處理

我們改進了 ResponseRequestBlobnode:fs 中記憶體不足錯誤的處理方式。先前,如果操作超出引擎限制,Bun 會崩潰或可能截斷,現在 Bun 將檢查操作是否肯定會超出限制,如果超出則正確地拋出錯誤。

import { expect } from "bun:test";
const buf = Buffer.alloc(4294967295, "abc");
try {
  const blob = new Blob([buf, buf]);
  await blob.text();
} catch (e) {
  expect(e.message).toBe(
    "Cannot create a string longer than 2^32-1 characters",
  );
  expect(e.code).toBe("ERR_STRING_TOO_LONG");
}

// Before: `catch` block would not be called
// After: `catch` block is called

改進的記憶體不足錯誤處理會影響以下 API

  • BlobRequestResponse 上的 text()json()bytes()formData()arrayBuffer() 方法
  • fs.writeFile() & fs.readFile()

Node.js 相容性改進

已修正:fs.F_OKfs.R_OKfs.W_OK 和類似常數

我們修正了一個錯誤,其中 Bun 中未定義 node:fs 常數,例如 F_OK。這些常數在 Node.js 20 中已被棄用,轉而使用 fs.constants,但為了相容性原因仍有定義。

import fs from "node:fs";

console.log(fs.F_OK); // old way
console.log(fs.constants.F_OK); // new way

已修正:fs.readFile 記憶體和大小限制

Bun 中的字串和類型化陣列受到引擎 (JavaScriptCore) 的限制,長度為 2^32 個字元。Node.js/V8 對於字串的限制較低,對於類型化陣列的限制較高。

當使用 fs.readFile 讀取大於此限制的檔案時,先前 Bun 的行為不正確。在某些情況下,Bun 會由於不正確的 JavaScript 例外處理而崩潰,而在其他情況下,Bun 可能會傳回截斷的字串或類型化陣列。

現在,一旦得知檔案將無法從 JavaScript 讀取,Bun 就會拋出錯誤。這節省了您的記憶體和 CPU 時間,因為它避免在拋出錯誤之前將整個檔案讀取到記憶體中。

已修正:'ws' 模組中的回壓

我們修正了一個錯誤,在高負載下呼叫 WebSocket.send() 會導致訊息被多次傳送。這是由於當訊息被 socket 拒絕時,回壓處理不正確所致。

import { WebSocket } from "ws";

const ws = new WebSocket("ws://example.com");

ws.on("open", () => {
  for (let i = 0; i < 1_000; i++) {
    ws.send(`Hello, world! ${i}`);
  }
});

// Before:            | After:
// ...                | ...
// Hello, world! 999  | Hello, world! 997
// Hello, world! 999  | Hello, world! 998
// Hello, world! 999  | Hello, world! 999
// ...                | ...

這僅影響透過匯入 ws 套件建立的 WebSocket 客戶端,Bun 會將其變更為使用我們自己的 WebSocket 實作。

感謝 @cirospaciari 修正此錯誤!

Windows 上與 Node.js 的跨執行階段 IPC

我們修正了一個錯誤,在 Bun 和 Node.js 程序之間傳送訊息時,node:child_process 中的跨程序通訊 (IPC) 在 Windows 上無法運作。在某些情況下,這可能會導致某些建置工具在 Windows 上掛起。

感謝 @cirospaciari 修正此錯誤!

node:vm 中的 JIT 崩潰

我們修正了在 node:vm 環境中評估已 JIT 編譯的程式碼時可能發生的幾個崩潰問題。當 node:vm 環境中的 globalThis 物件與實際的 globalThis 物件不同時,就會發生這種情況。

import { Script } from "node:vm";

const script = new Script(`
  for (let i = 0; i < 1_000_000; i++) {
    performance.now();
  }
`);

script.runInContext(globalThis); // ok
script.runInContext({ performance }); // would crash

這僅在約一百萬次調用後的某些程式碼路徑中受到影響

  • performance.now()
  • TextEncoder.encode() & TextDecoder.decode()
  • crypto.randomUUID() & crypto.getRandomValues()
  • crypto.timingSafeEqual()

感謝 @dylan-conway 修正此錯誤!

錯誤修正

已修正:透過 Bun.serve() 緩衝數 GB 的資料

我們修正了一個錯誤,其中從 Bun.serve() 接收然後串流大型回應會導致回應被截斷。這是因為如果資料大於 2^32-1 位元組,則 flush 會被截斷。

感謝 @cirospaciari 修正此錯誤!

已修正:在捆綁時移動標籤樣板字面值

我們修正了一個捆綁器錯誤,其中標籤樣板字面值在捆綁期間會被錯誤地移動。這會導致樣板字面值在錯誤的範圍內評估,從而導致其拋出錯誤。

foo.ts
globalThis.foo = () => console.log("foo");
const bar = await import("./bar.ts");
bar.ts
console.log("bar");
export const bar = foo`bar`;
// Before: TypeError: undefined is not a function (near '...foo`)
// After: bar\nfoo

這是 Bun v1.1.18 中引入的迴歸問題,現在已修正。

感謝 @paperclover 修正此錯誤!

已修正:使用自訂 TLS 憑證時,fetch() 的 AbortSignal

我們修正了一個錯誤,其中使用自訂 TLS 憑證呼叫 fetch() 不會由於逾時而中止請求。Bun 支援在發出 fetch() 請求時使用 tls 選項,這在非瀏覽器環境中通常是必要的,在這些環境中您需要使用自訂 TLS 憑證。

const response = await fetch("https://example.com", {
  signal: AbortSignal.timeout(1000),
  tls: {
    ca: "...",
  },
});

感謝 @cirospaciari 修正此錯誤!

已修正:當 new Response 拋出錯誤時的記憶體洩漏

我們修正了一個記憶體洩漏問題,其中在 new Response 上設定自訂 statusText 不會被清除。我們還新增了更多測試,以確保捕獲到 RequestResponse 的洩漏。

已修正:匯入空的 .toml 檔案時崩潰

我們修正了一個錯誤,其中匯入空的 .toml 檔案會導致 Bun 崩潰。這也會影響某些 .json 檔案,例如 package.jsontsconfig.json

import config from "./config.toml";

console.log(config);
// Before: <crash>
// After: { enabled: true }

已修正:1.1.22 中 TLS socket 的迴歸問題

1.1.22 中引入的迴歸問題可能會導致由於 DNS 問題而無法連線 TLS socket 時發生崩潰,感謝 @cirospaciari,此問題已修正。

感謝 6 位貢獻者!