Явное управление ресурсами в JavaScript: ключевое слово using

Источник: «Explicit Resource Management in JS: The using Keyword»
Познакомьтесь с новым ключевым словом using в JavaScript для более эффективного управления ресурсами, вдохновлённого C# и Rust.

JavaScript скоро представит новое ключевое слово: using. В настоящее время оно находится на третьем этапе (из четырёх) процесса предложения TC39, это означает, что скоро его можно будет использовать в продакшене.

Эта функция вдохновлена схожими концепциями в C# и Rust, в частности системой владения Rust, автоматически управляющей очисткой ресурсов для предотвращения таких распространённых ошибок, как утечки памяти и использование после освобождения. Она может автоматически обрабатывать ресурсы, реализующие метод Symbol.dispose, когда они больше не нужны, что значительно упрощает управление ресурсами в приложениях.

Понимание Symbol.dispose

В JavaScript (и, как следствие, в TypeScript) Symbol.dispose — это новый глобальный символ, идентифицирующий объекты, предназначенные для управления ресурсами. Это объекты, имеющие ограниченный жизненный цикл и нуждающиеся в явной очистке после использования для освобождения системных ресурсов. Назначив функцию Symbol.dispose, вы определяете, как объект должен быть очищен.

const resource = {
[Symbol.dispose]: () => {
console.log("Resource has been cleaned up!");
},
};

Использование using для синхронного управления ресурсами

Ключевое слово using позволяет объявить ресурс, который должен быть автоматически утилизирован, как только он выйдет из области видимости. Это удобно для управления такими ресурсами, как дескрипторы файлов или сетевые соединения. Вот как его можно использовать:

{
const getResource = () => ({
[Symbol.dispose]: () => console.log('Resource is now disposed!')
});

using resource = getResource();
}
// Output: 'Resource is now disposed!'

Этот блок гарантирует, что при выходе из него будет вызван метод dispose ресурса, тем самым предотвращая утечку ресурсов.

Асинхронная утилизация ресурсов с await using

Для ресурсов, требующих асинхронной очистки, JavaScript поддерживает await using. Это поможет в операциях, которые должны выполнять асинхронные задачи в рамках очистки, например, закрытие соединений с базой данных или очистка буферов в файл.

Приведём пример использующий await using:

const getResourceAsync = () => ({
[Symbol.asyncDispose]: async () => {
await someAsyncCleanupFunction();
}
});

{
await using resource = getResourceAsync();
}

Практические примеры

Обработка подключений к базам данных

Соединения с базами данных — критически важный ресурс, который требует тщательного управления, чтобы не исчерпать пулы соединений и не держать ненужные блокировки.

Без using:

const connection = await getDatabaseConnection();
try {
// Запрос к базе данных
} finally {
await connection.close();
}

С using:

const getConnection = async () => {
const connection = await getDatabaseConnection();
return {
connection,
[Symbol.asyncDispose]: async () => await connection.close()
};
};

{
await using db = getConnection();
// Использование db.connection для запросов
}
// Здесь соединение автоматически закрывается

Управление файловыми обработчиками

Работа с файлами часто требует тщательного управления ресурсами, чтобы не оставлять открытых обработчиков файлов, которые могут блокировать файлы или потреблять память. Вот как можно работать с файлом с и без using:

Без using:

import { open } from "node:fs/promises";

let fileHandle;
try {
fileHandle = await open("example.txt", "r");
// Чтение или запись файла
} finally {
if (fileHandle) {
await fileHandle.close();
}
}

С using:

import { open } from "node:fs/promises";

const getFileHandle = async (path) => {
const fileHandle = await open(path, "r");
return {
fileHandle,
[Symbol.asyncDispose]: async () => await fileHandle.close()
};
};

{
await using file = getFileHandle("example.txt");
// Работа с файлом.fileHandle
}
// После этого блока файл автоматически закрывается

Заключение

Эта возможность не только сокращает количество шаблонного кода, но и помогает предотвратить распространённые ошибки программирования, связанные с управлением ресурсами, что приближает JavaScript к безопасности и удобству, характерному для таких языков, как Rust и C#.

Дополнительные материалы

Предыдущая Статья

Анимация элемента dialog

Следующая Статья

Как закоммитить многострочные сообщения в git commit