
Deno — это компания, ориентированная на развитие JavaScript. Мы верим, что JavaScript должен быть простым, мощным и приятным в использовании. Deno стремится модернизировать JavaScript и инструменты вокруг него, предоставляя нативную поддержку TypeScript и стирая границы между серверным и браузерным JavaScript с помощью API, основанных на веб-стандартах. Поэтому мы активно участвуем в развитии JavaScript-экосистемы и в работе комитетов по стандартам, таких как TC39, — ведь мы хотим сделать JavaScript лучше и эффективнее для всех.
Недавно прошла 108-я встреча TC39, на которой было продвинуто 9 предложений на разные стадии стандартизации — от сырых идей (Стадия 0) до полностью утвержденных возможностей (Стадия 4).
Вот краткий обзор этих предложений и того, что они могут привнести в JavaScript.
Стадия 4
-
Explicit Resource Management (
using) -
Array.fromAsync -
Error.isError
Стадия 3
-
Immutable
ArrayBuffer
Стадия 2
-
Random.Seeded -
Number.prototype.clamp
Стадия 1
-
Сохранение конечных нулей
-
Сравнения
-
Функции произвольных значений
❯ Стадия 4
Explicit Resource Management (using)
Новая конструкция using (и ее асинхронный вариант await using) добавляет детерминированное освобождение ресурсов — идея, вдохновленная такими языками программирования, как C# и Python.
Объекты могут реализовать специальные методы [Symbol.dispose]() или [Symbol.asyncDispose](), которые будут автоматически вызываться при выходе из блока кода.
Пример использования:
class FileHandle { constructor(name) { this.name = name; /* открыть файл... */ } [Symbol.dispose]() { console.log(`${this.name} closed`); /* закрыть файл */ } } function readFile() { { using file = new FileHandle("data.txt"); // Чтение файла... } // Здесь автоматически вызывается `file.[Symbol.dispose]()` } readFile(); // "data.txt closed"
Это обеспечивает очистку ресурсов (например, закрытие файлов или потоков) даже при возникновении исключения внутри блока. Такой подход делает управление ресурсами проще и надежнее.
Эта возможность уже поддерживается в Chrome 134, Firefox 134 и Deno v2.3.
В Deno ключевое слово using уже можно использовать для управления ресурсами — такими как файловые дескрипторы (Deno.File), сетевые соединения (Deno.Conn) и т.д. Например, в приведенном ниже примере HTTP-сервер автоматически останавливается после выполнения запроса:
using server = Deno.serve({ port: 8000 }, () => { return new Response("Hello, world!"); }); const response = await fetch("http://localhost:8000"); console.log(await response.text()); // "Hello, world!" // Здесь сервер автоматически останавливается благодаря ключевому слову `using`
Команда Deno также заинтересована в использовании ключевого слова using для упрощения передачи асинхронного контекста. На самом деле, текущая реализация поддержки асинхронного контекста в Deno (стадия 2) уже позволяет, например, автоматически добавлять HTTP-информацию в console.log. Однако сейчас, чтобы создать новый «спан» (span) для отслеживания выполнения, каждый раз приходится создавать отдельную функцию и запускать ее вручную. Это неудобно:
async function doWork() { const parent = tracer.startSpan("doWork"); return parent.run(async () => { console.log("doing some work..."); return true; }); }
Команда Deno предлагает использовать disposable-версию AsyncContext.Variable, что позволит с помощью ключевого слова using упростить этот код:
async function doWork() { using parent = tracer.startActiveSpan("doWork"); console.log("doing some work..."); return true; }
Как видите, кода стало значительно меньше.
Array.fromAsync
Array.fromAsync похож на Array.from, но работает с асинхронными итерируемыми объектами и возвращает Promise, который разрешается в итоговый массив. Он также поддерживает функцию преобразования и параметр thisArg, как и Array.from.
Например, если у нас есть асинхронный генератор значений, то мы можем написать следующее:
async function* generate() { yield await Promise.resolve(1); yield await Promise.resolve(2); } const nums = await Array.fromAsync(generate()); // [1, 2]
В этом примере Array.fromAsync(generate()) возвращает промис, который разрешается в массив [1, 2] после того, как все значения будут получены. Это заметно облегчает и делает понятнее типичные способы работы с асинхронными коллекциями.
Array.fromAsync уже поддерживается во всех браузерах, а также в Deno v1.38 и Node.js v22.
Error.isError
Error.isError(value) — новый встроенный метод для надежного определения объектов ошибок. Он возвращает true, если value — любой тип ошибки (включая ошибки из других контекстов выполнения или наследуемые ошибки), и false в остальных случаях. Например:
Error.isError(new TypeError("oops")); // true Error.isError({ name: "TypeError", message: "oops" }); // false
Хотя такая проверка требуется нечасто, без нее бывает сложно писать некоторый код, например полифиллы.
Error.isError поддерживается во всех браузерах, а также в Deno v2.2.
❯ Стадия 3
Иммутабельный ArrayBuffer
Эта возможность находится на стадии 3 и вводит два новых метода: transferToImmutable() и sliceToImmutable(). Вызов transferToImmutable() перемещает данные из текущего буфера в новый неизменяемый буфер и отключает (detaches) оригинал. Например:
let buf = new ArrayBuffer(100); let imm = buf.transferToImmutable(); // buf теперь отключен (его byteLength равен 0), а imm — новый неизменяемый ArrayBuffer длиной 100 console.log(buf.byteLength, imm.byteLength); // 0, 100 // Попытка изменить imm вызывает TypeError imm[0] = 1; // TypeError: Cannot modify an immutable ArrayBuffer
Аналогично, метод sliceToImmutable(start, end) создает неизменяемую копию буфера указанного диапазона. Неизменяемые буферы нельзя отключить или изменить — благодаря этому передача бинарных данных (например, между потоками или воркерами) становится надежнее и эффективнее.
В Deno планируют использовать эту возможность для оптимизации различных API, которые принимают байтовые массивы — таких как new Response() или Deno.writeFile(). Это поможет сократить лишние копирования и улучшить производительность при работе с бинарными данными.
❯ Стадия 2
Random.Seeded
Текущие методы генерации псевдослучайных чисел (например, Math.random()) автоматически инициализируются (seeded), из-за чего результат не повторяется между запусками или разными контекстами выполнения. Однако бывают ситуации, когда нужен воспроизводимый набор произвольных значений.
В рамках этого предложения вводится новый класс SeededPRNG, который обеспечивает воспроизводимую генерацию произвольных чисел за счет возможности задания начального значения. Создаем объект Random.Seeded(seedValue) и используем его метод .random() вместо Math.random(), например:
const prng = new Random.Seeded(42); for (let i = 0; i < 3; i++) { console.log(prng.random()); // При seed === 42 при каждом запуске выводится одна и та же последовательность }
Это пригодится в играх и симуляциях, где важна повторяемость. Кроме того, можно создавать новые начальные значения или копировать состояние существующего генератора для более сложных задач.
Number.prototype.clamp
Функция Number.prototype.clamp(min, max) (ранее известная как Math.clamp) возвращает число в диапазоне от min до max. Например:
(5).clamp(0, 10); // 5 (-5).clamp(0, 10); // 0 (ограничено снизу - 0) (15).clamp(0, 10); // 10 (ограничено сверху - 10)
Если min больше max, функция выбрасывает исключение RangeError. Это позволяет избежать громоздких конструкций вроде Math.min(Math.max(x, min), max) и делает код понятнее.
❯ Стадия 1
Сохранение конечных нулей
Новые параметры форматирования в Intl.NumberFormat позволят сохранять или удалять конечные нули в отформатированных числах. Это поможет показывать десятичные числа в более удобном и наглядном виде (например, суммы денег).
Настройка trailingZeroDisplay: "auto" (по умолчанию) сохраняет нули в соответствии с заданной точностью, а "stripIfInteger" убирает их, если число целое. Например:
// Сохраняем два знака после запятой (автоматическое сохранение нулей): new Intl.NumberFormat("en", { minimumFractionDigits: 2, trailingZeroDisplay: "auto", }) .format(1.5); // "1.50" // Убираем нули, если они не нужны: new Intl.NumberFormat("en", { minimumFractionDigits: 0, trailingZeroDisplay: "stripIfInteger", }) .format(2); // "2" (не "2.0")
Это позволяет разработчикам точнее управлять форматированием чисел (например, валюты или чисел с фиксированной точностью) без необходимости вручную модифицировать строки.
Сравнения
Предложение Comparisons направлено на стандартизацию способа создания наглядного отображения значений в JavaScript — аналогично util.inspect в Node.js или тому, как тестовые фреймворки показывают отличия (diff).
Цель — обеспечить тестовым библиотекам и консольным инструментам единый и согласованный способ генерации отличий, особенно в разных средах выполнения и контекстах.
Функции случайных значений
Это предложение вводит новое пространство имен Random с удобными методами, которые помогают избежать типичных ошибок при использовании произвольных значений.
Методы из пространства Random могут не только генерировать произвольные числовые значения, но и работать с коллекциями — принимать их и возвращать.
Вот несколько примеров:
// Случайное целое число от -5 до 5 Random.int(-5, 5); // -1 // Случайное число от 0 до 10 Random.number(0, 10); // 8 // Случайное число от 0 до 5 с шагом 0.1 Random.number(0, 5, 0.1); // 1.1 // Случайный выбор n элементов из массива const name = Random.take(["Alice", "Bob", "Carol"], 2); // ['Alice', 'Bob'] // С возвратом (элементы могут повторяться) Random.take(["Alice", "Bob", "Carol"], 2, { replace: true }); // ['Alice', 'Alice'] // С учетом весов Random.take(["Alice", "Bob", "Carol"], 2, { weights: [1, 1, 5] }); // ['Alice', 'Bob'] // Случайный выбор одного элемента Random.sample(["Alice", "Bob", "Carol"]); // 'Bob' // Тасование массива на месте Random.shuffle([1, 2, 3, 4]); // [4,2,1,3] // Возврат нового перемешанного массива const shuffled = Random.toShuffled([1, 2, 3, 4]);
Цель — сделать работу с произвольными значениями надежнее и чище, сократив количество типичных ошибок (например, промахов на единицу) и повысив предсказуемость кода.
❯ Планы на будущее
TC39 продолжает развивать и улучшать JavaScript, адаптируя язык под нужды современных разработчиков. Команда Deno поддерживает веб-стандарты и активно участвует в этих обсуждениях, чтобы улучшать сам язык и упрощать работу с ним — особенно в среде Deno (достаточно вспомнить поддержку async context propagation и встроенный OpenTelemetry API).
Следующее заседание TC39, на котором продолжится обсуждение этих предложений, запланировано на конец сентября.
Новости, обзоры продуктов и конкурсы от команды Timeweb.Cloud — в нашем Telegram-канале ↩

ссылка на оригинал статьи https://habr.com/ru/articles/927768/
Добавить комментарий