> ## Content Index
> Fetch the complete content index at: https://admin.kod.ru/llms.txt
> Use this file to discover other available public pages before exploring further.

# Bun.js vs Node.js: выбираем среду исполнения JavaScript
- URL: https://admin.kod.ru/bun-js-vs-node-js-ya-pr/
- Published: 2024-04-27T21:05:16.000Z
- Updated: 2024-05-03T10:36:25.000Z
- Description: Bun — это новая среда исполнения Javascript-кода, по сути аналог Node.js. Давайте сравним их начиная с общего обзора, заканчивая производительностью по различным метрикам и в разных ситуациях.
- Author: Яндекс Практикум
- Tags: Блоги

Релиз Bun v1.0 вызвал ажиотаж и споры в JavaScript-сообществе. Все начали сравнивать, что лучше: Bun или Node.js? Разбираемся вместе с Сергеем Константиновым, наставником на курсе [«Фронтенд-разработчик»](https://practicum.yandex.ru/frontend-developer/?utm%5Fsource=pr&utm%5Fmedium=content&utm%5Fcampaign=pr%5Fcontent%5Ffrontend-developer%5Fkod-durova&utm%5Fcontent=26%5F04%5F24).

Bun — это новая среда исполнения Javascript-кода, по сути аналог Node.js. Давайте сравним их начиная с общего обзора, заканчивая производительностью по различным метрикам и в разных ситуациях.

## С чего всё началось

Node.js появился в 2009 году и решал задачу исполнения JavaScript-кода на стороне сервера. С того времени и JS, и Node претерпели много изменений.

23 сентября 2023 года вышел релиз Bun 1.0\. Bun, как и Node, позволяет запускать JavaScript-код на стороне сервера. Действительно ли молодой Bun настолько хорош? И насколько Node может с ним конкурировать?

Обсуждая среды исполнения JS-кода, сложно обойти стороной Deno. Он был создан Раеном Далем (создателем Node.js), чтобы исправить ряд проблем, которые обнаружили в Node.

Deno стал безопасной средой исполнения JavaScript-кода. Например, он поддерживает TypeScript из коробки — это избавляет от необходимости лишних внешних зависимостей. Deno использует подход, ориентированный на безопасность. Это требует от разработчиков предоставлять разрешения при потенциально конфиденциальных операциях: доступ к файловой системе или сетевые подключения. Хотя Deno и выглядит привлекательной альтернативой Node.js, он не получил такого широкого распространения.

Основное внимание в статье будет уделено сравнению Node.js и Bun, но мы также рассмотрим и Deno в ряде кейсов.

### Bun vs Node: общий обзор

Node.js преимущественно написан на языке C++, а для Bun выбрали язык Zig. В своей архитектуре экосистема Node.js использует модульную парадигму. То есть для работы с WebSocket, TypeScript, тестирования ему необходимы библиотечные модули.

Напротив, Bun представляет собой среду «всё в одном»: поддержка WebAPI, пакетный менеджер, инструменты для тестирования и многое другое. Данные модули реализованы внутри Bun и, по словам разработчиков рантайм-среды, лучше оптимизированы, чем аналоги в Node.js.

### Движок JavaScript

- Node.js использует движок Google V8, который также лежит в основе браузера Chrome. Bun отдал своё предпочтение JavaScriptCore, который является достоянием Apple и лежит в основе WebKit (Safari).
- V8 и JSC имеют в своей реализации различные архитектуры и используют разные подходы к оптимизации.
- JSC отдаёт приоритет более быстрому запуску и уменьшению использования памяти при более медленном времени выполнения.
- С другой стороны, V8 отдаёт приоритет быстрому выполнению с большим числом оптимизаций для последующей работы в рантайме, что также может привести к большему использованию памяти.

**Благодаря этому Bun способен запускаться в 4 раза быстрее, чем Node.js:**

![](https://admin.kod.ru/content/images/2024/04/image-34.png)

На самом деле такой тест не говорит о превосходстве в скорости JSC по сравнению с V8 — каждая из сред имеет свои дополнительные обработки и проверки. Но можно провести сравнение JSC и V8 изолированно. Я воспользовался результатами Криса Хэй из его [ролика на Youtube](https://www.youtube.com/watch?v=8wTulvlllGQ&ref=admin.kod.ru). Там он реализовал простой компилятор на Rust, для использования V8, а также JSCore, который можно вызвать на любом компьютере от Apple.

Вот временные результаты прогона теста скорости запуска на “hello world”:

|        | Время запуска |
| ------ | ------------- |
| JSCore | 1.12 ± 0.18   |
| Bun    | 2.68 ± 0.37   |
| Deno   | 4.29 ± 0.55   |
| Node   | 9.27 ± 1.17   |

Мы видим, что JSC действительно работает быстрее и V8, и Bun. Значит, потенциально у Bun есть возможности для больших оптимизаций.

### Транспайлер TypeScript

Несмотря на свою популярность, Node.js не имеет встроенной поддержки TypeScript. Один из вариантов использования TypeScript на Node — установка модуля для TS и его настройка для транспиляции в JS с последующим исполнением.

Пример базовой настройки c использованием TS-Node:

1. **Установка**

```js
npm install --save-dev typescript ts-node

```

2\. **Конфигурация стартового скрипта.** В package.json установите следующий скрипт запуска приложения:

```js
{
  "scripts": {
    "start": "ts-node ./path/to/your/file.ts"
  }
}

```

3\. **Запуск.** С помощью описанного выше сценария вы сможете запускать приложение, написанное на TypeScript.

```js
npm start

```

Bun имеет интегрированный транспайлер, позволяющий напрямую запускать файлы .js, .ts, .jsx и .tsx, без дополнительной настройки. Также это позволяет бесшовно производить транспиляцию JavaScript-файлов — это обеспечивает их немедленное исполнение без дополнительных шагов.

```js
bun index.ts

```

Разница в скорости объяснима отсутствием предварительной транспиляции, необходимой для Node.js.

![](https://admin.kod.ru/content/images/2024/04/image-35.png)

### Совместимость с ESM и CommonJS

В JavaScript популярно использование двух систем работы с модулями: CommonJS и ESM.

**CommonJS** основан на использовании require и module.exports для синхронной обработки модулей с возможностью динамического импорта.

**ESM** — более современная версия, ставшая стандартом с выходом ECMAScript 6, использует import/export. ESM поддерживает статичный импорт в начале файла, а также асинхронное взаимодействие с модулями с помощью ленивой загрузки. Это означает, что модули могут быть загружены и выполнены, только когда они действительно нужны.

**NodeJS** [официально получил поддержку](https://nodejs.medium.com/announcing-core-node-js-support-for-ecmascript-modules-c5d6dc29b663?ref=admin.kod.ru) ESM начиная с версии 13.2.0\. Bun поддерживает как CommonJS, так и ES-модули, позволяет использовать оба типа импортов в одном файле, но не требует дополнительной настройки. В Node нужно указать `"type": "module”` в package.json, если вы хотите использовать ESM, либо воспользоваться расширением `.mjs`

### Работа с WebAPI

Браузерный WebAPI предоставляет удобные методы работы с сетью: fetch и WebSocket. Хотя это и стало стандартом в браузере, для работы в Node долгое время нужно было ставить дополнительные модули, например, node-fetch.

NodeJS получил поддержку fetch в экспериментальном режиме с релизом 18-й версии.

Bun же обеспечивает стабильную встроенную поддержку стандартных WebAPI: fetch, WebSocket и других. Собственная реализация этих WebAPI в Bun гарантирует, что они работают быстрее и надёжнее по сравнению с альтернативами.

```js
async function fetchPosts() {
  const response = await fetch("https://jsonplaceholder.typicode.com/posts");
  const posts = await response.json();
	return posts;
}

fetchPosts();

```

*Экспериментальная поддержка в Node 18 и старше. Стабильная поддержка Bun.*

### Горячая перезагрузка

Горячая перезагрузка, или hot reloading, — функция автоматического обновления или перезагрузки частей приложения в режиме реального времени, без необходимости полного перезапуска. Это неотъемлемая часть удобного опыта разработки.

**В экосистеме Node есть несколько вариантов её реализации:**

Использование nodemon, который полностью перезапускает процесс для обновления `nodemon index.js`

Node v18 получил экспериментальную поддержку флага   
`--watchnode --watch index.js`

Оба метода выполняют схожую функцию, но их работа может отличаться в зависимости от различных сред и сценариев. Например, `nodemon` может привести к сбоям, например, к разрыву соединений HTTP и WebSocket. Экспериментальный флаг `--watch` может не обладать рядом функций и имеет некоторые проблемы, описанные в issue на [github](https://github.com/nodejs/node/issues?q=--watch&ref=admin.kod.ru).

Bun значительно улучшает процесс разработки благодаря встроенной функции горячей перезагрузки. Для этого необходимо запустить Bun с флагом `--hot`: `bun --hot index.ts`

В отличие от методов Node, требующих полного перезапуска всего приложения, Bun перезагружает код на месте, не завершая старый процесс. Это гарантирует бесперебойность HTTP и WebSocket-соединений, а также сохранение состояния приложения. Это обеспечивает удобство разработки.

### Обратная совместимость Bun с NodeJS

Bun позиционирует себя как замену для Node.js и стремится к 100% совместимости с существующими приложениями Node.js и npm-пакетами. Уже сейчас он поддерживает большинство npm-пакетов и встроенные библиотеки в Node:

- `fs`, `http`, `path` и другие.
- переменные `__dirname`, **`__`**[**filename**](https://nodejs.org/api/globals.html?ref=admin.kod.ru#%5F%5Ffilename)`**,**process`

Также команда Bun активно обрабатывает запросы, когда возникают проблемы с обратной совместимостью.

## **Работа с NodeJS API в Windows**

С выходом версии v1.1 Bun получил полную поддержку Windows. С этих пор обе runtime-среды поддерживают все популярные серверные платформы. Однако благодаря низкоуровневым оптимизациям Bun скорость работы в Windows с Node API быстрее на 58%.

![](https://admin.kod.ru/content/images/2024/04/image-36.png)

Также Bun стал кроссплатформенной оболочкой, в том числе и на Windows. Так, вы можете взаимодействовать с операционной системой посредством bun shell прямо из JavaScript:

```js
import { $ } from "bun";

// pipe to stdout:
await $`ls *.js`;

// pipe to string:
const text = await $`ls *.js`.text();

```

Для реализации подобного функционала в Node вам потребовался бы модуль child\_process.

```js
const { exec } = require('child_process');

exec('ls *.js', (error, stdout, stderr) => {
    if (error) throw error;
    console.log(stdout); // результат выполнения команды ls *.js
});

```

### Улучшенный API Bun чтения/записи по сравнению с Node.js

Bun предлагает оптимизированные API для работы с файлами, записи на диск и настройки HTTP-серверов — всё это значительно быстрее, чем в Node.js.

**Bun.file()**   
Позволяет получить доступ к файлам, реализуя ленивую загрузку. Этот метод работает до 10 раз быстрее, чем его аналог Node.js.

```js
// Bun 
const file = Bun.file("package.json");
await file.text();

// Node.js
const fs = require("fs/promises");
const fileContents = await fs.readFile("package.json", "utf-8");

```

**Bun.write()**   
Реализует запись данных на диск. Может работать как со строками, так и с большими двоичными объектами (blob). Производит запись в 3 раза быстрее, чем аналог в Node.

```js
// Bun
await Bun.write("index.html", "<html/>");

// Node.js
const fs = require("fs/promises");
await fs.writeFile("index.html", "<html/>");

```

**Bun.serve()**  
Позволяет установить http или websocket-сервер.

```js
// Bun
Bun.serve({
    port: 3000,
    fetch(request) {
      return new Response("Bun!");
    },
  });
  
  // Node.js
  import http from "http";
  const server = http.createServer((req, res) => {
    res.writeHead(200, { "Content-Type": "text/plain" });
    res.end("Node.js!");
  });
  server.listen(3000);

```

Как утверждают разработчики Bun, http-сервер обрабатывает в 2,5 раза больше запросов в секунду, чем Node.js. А также — в 7 раз больше сообщений по WebSocket, чем пакет ws в Node.js.

![](https://admin.kod.ru/content/images/2024/04/image-37.png)

Но так ли это на самом деле? Для ответа на этот вопрос, я обратился к докладу Виктора Хомякова c HolyJS. Он утверждает, что бенчмарки проводимые командой Bun заведомо неверные:

- Размеры заголовков и ответов сервера различаются. 202 байта в Bun, против 242 в Node.js
- Различный механизм работы сервера - Bun использует uWebSockets (микро-вебсокеты) в реализации Bun.serve() о чем сообщает Jarred в обсуждении на [GitHub](https://github.com/uNetworking/uWebSockets/discussions/1466?ref=admin.kod.ru#discussioncomment-3114410)
- Обе сборки должны работать в production режиме, что означает минифицированный код без всего лишнего

При запуске бенчмарков в равных условиях тестирование RPS на Ubuntu с AMD EPYC дает нам одинаковый результат:

|           | RPS    | CPU  |
| --------- | ------ | ---- |
| Bun serve | 48-50k | 40%  |
| Bun http  | 32-36k | 60%  |
| Node http | 22-35k | 100% |
| Node uWS  | 48-50k | 40%  |

### Bun против Node.js: менеджер пакетов

Bun также предоставляет пакетный менеджер — более быструю альтернативу, чем npm, yarn и pnpm. Как заявляют разработчики Bun, замена npm install на bun install ускорит установку ваших пакетов более чем в **25** раз.

![](https://admin.kod.ru/content/images/2024/04/image-38.png)

Давайте сравним API Bun с аналогичным в npm:

| Bun                  | npm                      |                                            |
| -------------------- | ------------------------ | ------------------------------------------ |
| bun install          | npm install              | Устанавливает зависимости из package.json  |
| bun add <package>    | npm install <package>    | Добавляет новую зависимость в проект       |
| bun add <package> -D | npm install <package> -D | Добавляет новую зависимость для разработки |
| bun remove <package> | npm uninstall <package>  | Удаляет пакет из проекта                   |
| bun update <package> | npm update <package>     | Обновляет пакет до последней версии        |
| bun run <script>     | npm run <script>         | Запускает скрипт из package.json           |

Скорость установки пакетов с помощью Bun превышает скорость установки через npm. Это происходит благодаря использованию глобального кэша модулей, исключая излишние загрузки из npm registry. Также Bun использует максимально быстрые системные вызовы, присутствующие в каждой системе, такие как хардлинки. Как следствие, Bun также экономит место на диске, так как все модули находятся в едином месте: \~/.bun/install/cache.

![](https://admin.kod.ru/content/images/2024/04/image-39.png)

Из недостатков отметим, что вместо package-lock Bun использует свои бинарные файлы. Они предоставляют дополнительную оптимизацию, но ухудшают читабельность.

### Bun как сборщик

Bun имеет встроенный сборщик, который превосходит современные решения, используемые для Node.js. В экосистеме Node.js процессы сборки, транспиляции, минификации кода реализуют комплексом инструментов. Bun же полностью контролирует сборку, реализуя её с помощью своей внутренней экосистемы.

Запустить билд можно командой:

```js
bun build ./index.ts --outdir ./build

```

Сравним скорость с помощью теста esbuild's [three.js benchmark](https://github.com/oven-sh/bun/tree/main/bench/bundle?ref=admin.kod.ru).

![](https://admin.kod.ru/content/images/2024/04/image-40.png)

Отличительной особенностью сборки Bun являются макросы. Макросы — это механизм запуска функций JavaScript во время сборки. Значение, возвращаемое этими функциями, напрямую встраивается в результат сборки.

Например, мы можем произвести некоторые вычисления, которые будут подставлены в результат сборки.

```js
// whenever.ts
export function random() {
    return Math.random();
}

// cli.ts
import { random } from './random.ts' with { type: 'macro' };
console.log(`Your random number is ${random()}`);

```

Исходный код функции random отсутствует в финальном бандле. Вместо этого он исполнился во время сборки, и вызов функции `random()` был заменён результатом выполнения. Поскольку исходный код никогда не будет включён в пакет, макросы могут безопасно выполнять привилегированные операции (чтение из базы данных).

> **Примечание:** Макросы обозначаются с использованием атрибутов импорта, что позволяет прикрепить дополнительные метаданные при импорте. Подробнее об этом предложении вы можете почитать на [странице github](https://github.com/tc39/proposal-import-attributes?ref=admin.kod.ru).

### Инструмент для тестирования Bun

Bun в ряде своих инструментов имеет среду для тестирования. Синтаксис похож на уже привычный нам Jest, что сильно упрощает миграцию.

```js
test("Bun test", () => {
  expect('hello' + ' '+ 'world').toBe('hello world');
});

```

### Производительность в тестах

Со слов разработчиков рантайм среды, в тестах Bun в 13 раз превосходит по скорости Jest, в 8 раз Vitest. Это достигнуто с помощью методов, имеющих низкоуровневую реализацию. Например, метод `.toEqual()` в Bun работает в 100 раз быстрее, чем Jest, и в 10 раз, чем Vitest.

### Подводим итоги

- Bun вызывает серьёзный интерес в сообществе.
- Поддержка всех платформ уже на достаточно высоком уровне. All-in-one решение и использование современных технологий делают его выигрышным выбором для повышения производительности JavaScript-приложений.
- Однако, несмотря на бенчмарки и перфоманс-тесты, не стоит утверждать, что Node не может конкурировать с Bun.
- В реальности синтетические тесты производительности Bun, как утверждает Виктор Хомяков, — липа и к ним стоит относиться скептически. Это объясняется тем, что тесты производительности гоняются на программах вида hello world в синтетически созданных, и часто неравных условиях. Это не может дать объективной оценки. На деле мы получаем сравнимо схожую производительность обеих систем.
- Node.JS до сих пор является лидером по однопоточной обработке среди других серверных решений. И имеет большое комьюнити — значит, и большое число решённых проблем, что делает его более привлекательным для выбора в крупных компаниях, которым важна стабильность.
- Bun предстоит пройти проверку временем. Тем не менее Bun — отличный инструмент, чтобы его начать использовать на новых небольших проектах.