Привет! Представляю вашему вниманию перевод статьи Matt Pocock.
Источник
TypeScript 5.2 представит новое ключевое слово - using
, которое можно использовать, чтобы избавиться от чего угодно с помощью функции Symbol.dispose
, при покидании области видимости.
{
const getResource = () => {
return {
[Symbol.dispose]: () => {
console.log('Hooray!')
}
}
}
using resource = getResource();
} // 'Hooray!' logged to console
Обосновано это предложениями TC39, достигшими недавно Фазы 3, что демонстрирует скорое появление в JavaScript.
using
будет очень полезным для управления ресурсами, такими как ссылки на файлы, соединения с базами данных и т.п.
Symbol.dispose
Symbol.dispose
- новый глобальный символ в JavaScript. Всё что угодно с функцией, присвоеннойSymbol.dispose
будет рассматриваться как 'ресурс' - "объект с особым жизненным циклом" - и может быть использовано со словом using
.
const resource = {
[Symbol.dispose]: () => {
console.log("Hooray!");
},
};
await using
Вы также можете использовать Symbol.asyncDispose
и await using
чтобы управлять ресурсами, которые должны быть распределены асинхронно.
const getResource = () => ({
[Symbol.asyncDispose]: async () => {
await someAsyncFunc();
},
});
{
await using resource = getResource();
}
Будет ожидать функцию Symbol.asyncDispose
для продолжения.
Это будет полезно для ресурсов типа коннекторов БД, когда необходимо убедиться, что соединение закрыто, прежде чем продолжать.
Случаи использования
Указатели на файлы
Доступ к файловой системе с помощью хэндлеров на ноде может быть намного проще с using
.
До:
import { open } from "node:fs/promises";
let filehandle;
try {
filehandle = await open("thefile.txt", "r");
} finally {
await filehandle?.close();
}
После:
import { open } from "node:fs/promises";
const getFileHandle = async (path: string) => {
const filehandle = await open(path, "r");
return {
filehandle,
[Symbol.asyncDispose]: async () => {
await filehandle.close();
},
};
};
{
await using file = getFileHandle("thefile.txt");
// Do stuff with file.filehandle
} // Automatically disposed!
Соединения с БД
До:
const connection = await getDb();
try {
// Do stuff with connection
} finally {
await connection.close();
}
После:
const getConnection = async () => {
const connection = await getDb();
return {
connection,
[Symbol.asyncDispose]: async () => {
await connection.close();
},
};
};
{
await using { connection } = getConnection();
// Do stuff with connection
} // Automatically closed!
Спасибо за внимание! Больше интересного по фронтенду - тут.
Комментарии (9)
iliazeus
03.07.2023 11:45+8Если сходить не в какую-то статью, а в блог самого TypeScript, то можно найти там вторую - и очень важную часть нововведения:
DisposableStack
иAsyncDisposableStack
. С их помощью можно подчищать ресурсы, у которых ещё нет своего метода@@dispose
/@@asyncDispose
.function doSomeWork() { const path = ".some_temp_file"; const file = fs.openSync(path, "w+"); using cleanup = new DisposableStack(); cleanup.defer(() => { fs.closeSync(file); fs.unlinkSync(path); }); // use file... if (someCondition()) { // do some more work... return; } // ... }
iliazeus
03.07.2023 11:45+2Плюс, там же написана ещё одна важная вещь: TypeScript не будет автоматически полифиллить сами
Symbol.dispose
,Symbol.asyncDispose
,DisposableStack
,AsyncDisposableStack
. Полифиллы нужно подключать самому. Если стеки не нужны, а нужен толькоusing
, можно сделать просто:Symbol.dispose ??= new Symbol("Symbol.dispose"); Symbol.asyncDispose ??= new Symbol("Symbol.asyncDispose");
И работает это все пока только с таргетом
es2022
или ниже, иesnext
илиesnext.disposable
в lib.
yet_it Автор
03.07.2023 11:45Если сходить не в какую-то статью, а в блог самого TypeScript
Ну кстати я сам удивился - статья от 16 июня, блог разрабов от 30 июня
Есть идеи, почему так?)iliazeus
03.07.2023 11:45+1Возможно, автор следил за разработкой фичи в ишью на гитхабе, и писал ее, ориентируясь на информацию оттуда.
aamonster
03.07.2023 11:45Т.е. с поведением в точности как деструкторы C++ объектов на стеке, я правильно понял? А по реализации это синтаксический сахар над try-finally?
iliazeus
03.07.2023 11:45+3Грубо говоря, да - уничтожаются тоже в обратном порядке при выходе из скоупа, включая выход по исключению. Вот здесь есть подробнее о семантике.
Главное отличие, кажется, в том, что происходит, если
dispose
выбросил исключение - здесь вызов деструкторов продолжается, а все выброшенные исключения комбинируются в одно и выбрасываются после.
iliazeus
Если сходить не в какую-то статью, а в блог самого TypeScript, то можно найти там вторую - и очень важную часть нововведения:
DisposableStack
иAsyncDisposableStack
. С их помощью можно подчищать ресурсы, у которых ещё нет своего метода@@dispose
/@@asyncDispose
.