Từ khóa using trong TypeScript và Quản lý Tài nguyên Tường minh
Rò rỉ bộ nhớ trong môi trường production thường bắt nguồn từ một lỗi duy nhất: lập trình viên chiếm dụng tài nguyên nhưng không giải phóng chúng.
Các kết nối cơ sở dữ liệu vẫn mở sau khi xảy ra lỗi. Các file handle tiêu tốn tài nguyên hệ thống. Các WebSocket client vẫn kết nối với các máy chủ đã ngừng hoạt động. Điều này xảy ra khi bạn dựa vào việc dọn dẹp thủ công trong các khối finally.
Từ khóa using trong TypeScript giải quyết vấn đề này. Nó là một phần của đề xuất Explicit Resource Management trong ECMAScript. Nó đảm bảo việc dọn dẹp thông qua pattern "disposable" (có thể giải phóng). Khi một tài nguyên thoát khỏi phạm vi (scope) của nó, TypeScript sẽ tự động chạy phương thức giải phóng (disposal method).
Bạn không còn cần các khối finally thủ công. Bạn không còn quên dọn dẹp. Bạn không còn làm rò rỉ các kết nối.
Cách thức hoạt động:
- Từ khóa
usingđảm bảo việc giải phóng khi tài nguyên thoát khỏi một scope. - Các tài nguyên có thể giải phóng (disposable resources) triển khai
Symbol.disposecho các tác vụ đồng bộ hoặcSymbol.asyncDisposecho các tác vụ bất đồng bộ. - TypeScript chuyển đổi các khai báo
usingthành các khốitry-finallyvới một ngăn xếp giải phóng (disposal stack) tự động. - Pattern này ngăn chặn việc rò rỉ do return sớm, ném ra ngoại lệ (exceptions) hoặc quên viết code dọn dẹp.
Hãy sử dụng using cho bất kỳ tài nguyên nào có vòng đời xác định, chẳng hạn như kết nối cơ sở dữ liệu, file handle, khóa (locks) hoặc bộ hẹn giờ (timers).
Cơ chế này sử dụng một giao thức giải phóng (disposal protocol). Các đối tượng triển khai một phương thức được định danh bởi Symbol.dispose. Khi scope kết thúc thông qua việc hoàn thành bình thường, một lệnh return, hoặc một ngoại lệ, TypeScript sẽ gọi phương thức đó.
Ví dụ về một file handle:
class FileHandle {
private handle: number;
constructor(path: string) {
this.handle = openFileSync(path);
}
[Symbol.dispose]() {
if (this.handle !== -1) {
closeFileSync(this.handle);
this.handle = -1;
}
}
read(buffer: Buffer): number {
return readSync(this.handle, buffer);
}
}
function processFile(path: string) {
using file = new FileHandle(path);
const buffer = Buffer.alloc(1024);
file.read(buffer);
}
Việc giải phóng được thực hiện trong một khối finally được tạo ra tự động. Nó vẫn chạy ngay cả khi hàm ném ra lỗi hoặc kết thúc sớm.
TypeScript duy trì một ngăn xếp giải phóng (disposal stack). Nó giải phóng các tài nguyên theo thứ tự ngược lại với thứ tự mà bạn đã chiếm dụng chúng. Điều này phù hợp với thứ tự phụ thuộc tự nhiên của hầu hết các logic dọn dẹp.
Để dọn dẹp bất đồng bộ, hãy sử dụng await using. Điều này yêu cầu tài nguyên phải triển khai Symbol.asyncDispose. TypeScript sẽ await Promise được trả về trước khi tiếp tục.
Ba quy tắc để thành công:
- Không ném ngoại lệ bên trong các phương thức giải phóng của bạn. Thay vào đó, hãy bắt (catch) và ghi log lỗi nội bộ.
- Không sử dụng các thao tác bất đồng bộ bên trong phương thức
Symbol.disposeđồng bộ. Hãy sử dụngSymbol.asyncDisposethay thế. - Hãy nhớ rằng các khai báo
usingcó phạm vi trong khối (block-scoped). Một tài nguyên bên trong khốiifsẽ được giải phóng khi khối đó kết thúc, chứ không phải khi hàm kết thúc.
Pattern này mang lại sự an toàn với chi phí runtime không đáng kể.
Source: https://dev.to/jsmanifest/typescript-using-keyword-and-explicit-resource-management-done-right-22pg
