Tìm hiểu sâu về quản lý tài nguyên nâng cao trong JavaScript. Học cách kết hợp khai báo 'using' sắp tới với nhóm tài nguyên để có các ứng dụng sạch hơn, an toàn hơn và hiệu suất cao.
Làm Chủ Quản Lý Tài Nguyên: Câu Lệnh 'using' của JavaScript và Chiến Lược Nhóm Tài Nguyên
Trong thế giới JavaScript phía máy chủ hiệu suất cao, đặc biệt là trong các môi trường như Node.js và Deno, quản lý tài nguyên hiệu quả không chỉ là một thông lệ tốt nhất; nó là một thành phần quan trọng để xây dựng các ứng dụng có khả năng mở rộng, phục hồi và hiệu quả về chi phí. Các nhà phát triển thường vật lộn với việc quản lý các tài nguyên hạn chế, tốn kém để tạo ra như kết nối cơ sở dữ liệu, trình xử lý tệp, ổ cắm mạng hoặc luồng công nhân. Xử lý sai các tài nguyên này có thể dẫn đến một loạt các vấn đề: rò rỉ bộ nhớ, cạn kiệt kết nối, mất ổn định hệ thống và hiệu suất suy giảm.
Theo truyền thống, các nhà phát triển đã dựa vào khối try...catch...finally
để đảm bảo tài nguyên được dọn dẹp. Mặc dù hiệu quả, nhưng mẫu này có thể dài dòng và dễ xảy ra lỗi. Mặt khác, để có hiệu suất, chúng ta sử dụng nhóm tài nguyên để tránh chi phí phát sinh khi liên tục tạo và phá hủy các tài sản này. Nhưng làm thế nào để chúng ta kết hợp một cách thanh lịch sự an toàn của việc dọn dẹp được đảm bảo với hiệu quả của việc tái sử dụng tài nguyên? Câu trả lời nằm ở sự hợp lực mạnh mẽ giữa hai khái niệm: một mẫu gợi nhớ đến using
statement được tìm thấy trong các ngôn ngữ khác và chiến lược đã được chứng minh về resource pooling.
Hướng dẫn toàn diện này sẽ khám phá cách kiến trúc một chiến lược quản lý tài nguyên mạnh mẽ trong JavaScript hiện đại. Chúng ta sẽ đi sâu vào đề xuất TC39 sắp tới để quản lý tài nguyên rõ ràng, giới thiệu các từ khóa using
và await using
, và chứng minh cách tích hợp cú pháp khai báo, rõ ràng này với một nhóm tài nguyên tùy chỉnh để xây dựng các ứng dụng vừa mạnh mẽ vừa dễ bảo trì.
Hiểu Vấn Đề Cốt Lõi: Quản Lý Tài Nguyên trong JavaScript
Trước khi chúng ta xây dựng một giải pháp, điều quan trọng là phải hiểu các sắc thái của vấn đề. Chính xác thì 'tài nguyên' là gì trong bối cảnh này và tại sao việc quản lý chúng lại khác với việc quản lý bộ nhớ đơn giản?
Tài Nguyên' Là Gì?
Trong cuộc thảo luận này, 'tài nguyên' đề cập đến bất kỳ đối tượng nào giữ kết nối đến một hệ thống bên ngoài hoặc yêu cầu một hoạt động 'đóng' hoặc 'ngắt kết nối' rõ ràng. Chúng thường bị giới hạn về số lượng và tốn kém về mặt tính toán để thiết lập. Các ví dụ phổ biến bao gồm:
- Kết Nối Cơ Sở Dữ Liệu: Thiết lập kết nối đến cơ sở dữ liệu bao gồm bắt tay mạng, xác thực và thiết lập phiên, tất cả đều tiêu tốn thời gian và chu kỳ CPU.
- Trình Xử Lý Tệp: Hệ điều hành giới hạn số lượng tệp mà một quy trình có thể mở đồng thời. Rò rỉ trình xử lý tệp có thể ngăn ứng dụng mở các tệp mới.
- Ổ Cắm Mạng: Kết nối đến các API bên ngoài, hàng đợi tin nhắn hoặc các dịch vụ vi mô khác.
- Luồng Công Nhân hoặc Quy Trình Con: Tài nguyên tính toán nặng nên được quản lý trong một nhóm để tránh chi phí tạo quy trình.
Tại Sao Bộ Thu Gom Rác Không Đủ
Một quan niệm sai lầm phổ biến giữa các nhà phát triển mới làm quen với lập trình hệ thống là bộ thu gom rác (GC) của JavaScript sẽ xử lý mọi thứ. GC rất tuyệt vời trong việc thu hồi bộ nhớ bị chiếm giữ bởi các đối tượng không còn có thể truy cập được. Tuy nhiên, nó không quản lý tài nguyên bên ngoài một cách tất định.
Khi một đối tượng đại diện cho một kết nối cơ sở dữ liệu không còn được tham chiếu, GC cuối cùng sẽ giải phóng bộ nhớ của nó. Nhưng nó không đảm bảo về khi nào điều này sẽ xảy ra, cũng như nó không biết rằng nó cần gọi một phương thức .close()
để giải phóng ổ cắm mạng cơ bản trở lại hệ điều hành hoặc khe kết nối trở lại máy chủ cơ sở dữ liệu. Dựa vào GC để dọn dẹp tài nguyên dẫn đến hành vi không tất định và rò rỉ tài nguyên, nơi ứng dụng của bạn giữ các kết nối quý giá lâu hơn mức cần thiết.
Mô Phỏng Câu Lệnh 'using': Một Con Đường Dẫn Đến Dọn Dẹp Tất Định
Các ngôn ngữ như C# (với using
) và Python (với with
) cung cấp cú pháp thanh lịch để đảm bảo rằng logic dọn dẹp tài nguyên được thực thi ngay khi nó ra khỏi phạm vi. Khái niệm này được gọi là deterministic resource management. JavaScript đang trên bờ vực có một giải pháp gốc, nhưng trước tiên hãy xem phương pháp truyền thống.
Cách Tiếp Cận Cổ Điển: Khối try...finally
Con ngựa thồ để quản lý tài nguyên trong JavaScript luôn là khối try...finally
. Mã trong khối finally
được đảm bảo thực thi, bất kể mã trong khối try
hoàn thành thành công, ném ra lỗi hay trả về một giá trị.
Đây là một ví dụ điển hình để quản lý kết nối cơ sở dữ liệu:
async function getUserById(id) {
let connection;
try {
connection = await getDatabaseConnection(); // Acquire resource
const result = await connection.query('SELECT * FROM users WHERE id = ?', [id]);
return result[0];
} catch (error) {
console.error("An error occurred during the query:", error);
throw error; // Re-throw the error
} finally {
if (connection) {
await connection.close(); // ALWAYS release resource
}
}
}
Mẫu này hoạt động, nhưng nó có những hạn chế:
- Dài Dòng: Mã boilerplate để có được và giải phóng tài nguyên thường làm lu mờ logic nghiệp vụ thực tế.
- Dễ Xảy Ra Lỗi: Rất dễ quên kiểm tra
if (connection)
hoặc xử lý sai các lỗi trong chính khốifinally
. - Độ Phức Tạp Lồng Nhau: Quản lý nhiều tài nguyên dẫn đến các khối
try...finally
lồng nhau sâu sắc, thường được gọi là "kim tự tháp diệt vong."
Một Giải Pháp Hiện Đại: Đề Xuất Khai Báo 'using' TC39
Để giải quyết những thiếu sót này, ủy ban TC39 (nơi tiêu chuẩn hóa JavaScript) đã thúc đẩy Đề Xuất Quản Lý Tài Nguyên Rõ Ràng. Đề xuất này, hiện đang ở Giai đoạn 3 (nghĩa là nó là một ứng cử viên để đưa vào tiêu chuẩn ECMAScript), giới thiệu hai từ khóa mới—using
và await using
—và một cơ chế để các đối tượng xác định logic dọn dẹp của riêng chúng.
Cốt lõi của đề xuất này là khái niệm về một tài nguyên "có thể vứt bỏ". Một đối tượng trở nên có thể vứt bỏ bằng cách triển khai một phương thức cụ thể dưới một khóa Symbol nổi tiếng:
[Symbol.dispose]()
: Đối với logic dọn dẹp đồng bộ.[Symbol.asyncDispose]()
: Đối với logic dọn dẹp không đồng bộ (ví dụ: đóng kết nối mạng).
Khi bạn khai báo một biến với using
hoặc await using
, JavaScript sẽ tự động gọi phương thức dispose tương ứng khi biến đó ra khỏi phạm vi, hoặc ở cuối khối hoặc nếu một lỗi được ném ra.
Hãy tạo một trình bao bọc kết nối cơ sở dữ liệu có thể vứt bỏ:
class ManagedDatabaseConnection {
constructor(connection) {
this.connection = connection;
this.isDisposed = false;
}
// Expose database methods like query
async query(sql, params) {
if (this.isDisposed) {
throw new Error("Connection is already disposed.");
}
return this.connection.query(sql, params);
}
async [Symbol.asyncDispose]() {
if (!this.isDisposed) {
console.log('Disposing connection...');
await this.connection.close();
this.isDisposed = true;
console.log('Connection disposed.');
}
}
}
// How to use it:
async function getUserByIdWithUsing(id) {
// Assumes getRawConnection returns a promise for a connection object
const rawConnection = await getRawConnection();
await using connection = new ManagedDatabaseConnection(rawConnection);
const result = await connection.query('SELECT * FROM users WHERE id = ?', [id]);
return result[0];
// No finally block needed! `connection[Symbol.asyncDispose]` is called automatically here.
}
Hãy nhìn vào sự khác biệt! Ý định của mã rất rõ ràng. Logic nghiệp vụ ở ngay trước mắt và việc quản lý tài nguyên được xử lý tự động và đáng tin cậy ở phía sau. Đây là một cải tiến to lớn về độ rõ ràng và an toàn của mã.
Sức Mạnh của Nhóm: Tại Sao Phải Tạo Lại Khi Bạn Có Thể Tái Sử Dụng?
Mẫu using
giải quyết vấn đề *dọn dẹp được đảm bảo*. Nhưng trong một ứng dụng có lưu lượng truy cập cao, việc tạo và phá hủy một kết nối cơ sở dữ liệu cho mọi yêu cầu là vô cùng kém hiệu quả. Đây là nơi nhóm tài nguyên xuất hiện.
Nhóm Tài Nguyên Là Gì?
Một nhóm tài nguyên là một mẫu thiết kế duy trì một bộ nhớ cache các tài nguyên sẵn sàng sử dụng. Hãy nghĩ về nó như bộ sưu tập sách của một thư viện. Thay vì mua một cuốn sách mới mỗi khi bạn muốn đọc một cuốn sách và sau đó vứt nó đi, bạn mượn một cuốn từ thư viện, đọc nó và trả lại cho người khác sử dụng. Điều này hiệu quả hơn nhiều.
Một triển khai nhóm tài nguyên điển hình bao gồm:
- Khởi Tạo: Nhóm được tạo với số lượng tài nguyên tối thiểu và tối đa. Nó có thể tự động điền số lượng tài nguyên tối thiểu.
- Có Được: Một máy khách yêu cầu một tài nguyên từ nhóm. Nếu một tài nguyên có sẵn, nhóm sẽ cho nó mượn. Nếu không, máy khách có thể đợi cho đến khi một tài nguyên có sẵn hoặc nhóm có thể tạo một tài nguyên mới nếu nó dưới giới hạn tối đa của nó.
- Giải Phóng: Sau khi máy khách hoàn thành, nó trả lại tài nguyên cho nhóm thay vì phá hủy nó. Sau đó, nhóm có thể cho máy khách khác mượn cùng một tài nguyên này.
- Phá Hủy: Khi ứng dụng tắt, nhóm sẽ đóng tất cả các tài nguyên mà nó quản lý một cách duyên dáng.
Lợi Ích của Nhóm
- Giảm Độ Trễ: Có được một tài nguyên từ một nhóm nhanh hơn đáng kể so với việc tạo một tài nguyên mới từ đầu.
- Giảm Chi Phí Phát Sinh: Giảm áp lực CPU và bộ nhớ trên cả máy chủ ứng dụng và hệ thống bên ngoài (ví dụ: cơ sở dữ liệu).
- Điều Chỉnh Kết Nối: Bằng cách đặt kích thước nhóm tối đa, bạn ngăn ứng dụng của mình áp đảo cơ sở dữ liệu hoặc dịch vụ bên ngoài với quá nhiều kết nối đồng thời.
Tổng Hợp Vĩ Đại: Kết Hợp `using` với Nhóm Tài Nguyên
Bây giờ chúng ta đến cốt lõi của chiến lược của chúng ta. Chúng ta có một mẫu tuyệt vời để dọn dẹp được đảm bảo (using
) và một chiến lược đã được chứng minh về hiệu suất (nhóm). Làm thế nào để chúng ta hợp nhất chúng thành một giải pháp liền mạch, mạnh mẽ?
Mục tiêu là có được một tài nguyên từ nhóm và đảm bảo rằng nó được trả lại cho nhóm khi chúng ta hoàn thành, ngay cả khi đối mặt với lỗi. Chúng ta có thể đạt được điều này bằng cách tạo một đối tượng trình bao bọc triển khai giao thức dispose, nhưng phương thức `dispose` của nó gọi `pool.release()` thay vì `resource.close()`.
Đây là liên kết kỳ diệu: hành động `dispose` trở thành 'trả về nhóm' thay vì 'phá hủy'.
Triển Khai Từng Bước
Hãy xây dựng một nhóm tài nguyên chung và các trình bao bọc cần thiết để làm cho điều này hoạt động.
Bước 1: Xây Dựng Một Nhóm Tài Nguyên Chung, Đơn Giản
Đây là một triển khai khái niệm về một nhóm tài nguyên không đồng bộ. Một phiên bản sẵn sàng sản xuất sẽ có nhiều tính năng hơn như thời gian chờ, loại bỏ tài nguyên nhàn rỗi và logic thử lại, nhưng điều này minh họa các cơ chế cốt lõi.
class ResourcePool {
constructor({ create, destroy, min, max }) {
this.factory = { create, destroy };
this.config = { min, max };
this.pool = []; // Stores available resources
this.active = []; // Stores resources currently in use
this.waitQueue = []; // Stores promises for clients waiting for a resource
// Initialize minimum resources
for (let i = 0; i < this.config.min; i++) {
this._createResource().then(resource => this.pool.push(resource));
}
}
async _createResource() {
const resource = await this.factory.create();
return resource;
}
async acquire() {
// If a resource is available in the pool, use it
if (this.pool.length > 0) {
const resource = this.pool.pop();
this.active.push(resource);
return resource;
}
// If we are under the max limit, create a new one
if (this.active.length < this.config.max) {
const resource = await this._createResource();
this.active.push(resource);
return resource;
}
// Otherwise, wait for a resource to be released
return new Promise((resolve, reject) => {
// A real implementation would have a timeout here
this.waitQueue.push({ resolve, reject });
});
}
release(resource) {
// Check if someone is waiting
if (this.waitQueue.length > 0) {
const waiter = this.waitQueue.shift();
// Give this resource directly to the waiting client
waiter.resolve(resource);
} else {
// Otherwise, return it to the pool
this.pool.push(resource);
}
// Remove from active list
this.active = this.active.filter(r => r !== resource);
}
async close() {
// Close all resources in the pool and those active
const allResources = [...this.pool, ...this.active];
this.pool = [];
this.active = [];
await Promise.all(allResources.map(r => this.factory.destroy(r)));
}
}
Bước 2: Tạo Trình Bao Bọc 'PooledResource'
Đây là phần quan trọng kết nối nhóm với cú pháp using
. Nó sẽ giữ một tài nguyên và một tham chiếu đến nhóm mà nó đến từ đó. Phương thức dispose của nó sẽ gọi pool.release()
.
class PooledResource {
constructor(resource, pool) {
this.resource = resource;
this.pool = pool;
this._isReleased = false;
}
// This method releases the resource back to the pool
[Symbol.dispose]() {
if (this._isReleased) {
return;
}
this.pool.release(this.resource);
this._isReleased = true;
console.log('Resource released back to pool.');
}
}
// We can also create an async version
class AsyncPooledResource {
constructor(resource, pool) {
this.resource = resource;
this.pool = pool;
this._isReleased = false;
}
// The dispose method can be async if releasing is an async operation
async [Symbol.asyncDispose]() {
if (this._isReleased) {
return;
}
// In our simple pool, release is sync, but we show the pattern
await Promise.resolve(this.pool.release(this.resource));
this._isReleased = true;
console.log('Async resource released back to pool.');
}
}
Bước 3: Đặt Tất Cả Cùng Nhau trong Một Trình Quản Lý Thống Nhất
Để làm cho API thậm chí sạch hơn, chúng ta có thể tạo một lớp trình quản lý đóng gói nhóm và cung cấp các trình bao bọc có thể vứt bỏ.
class ResourceManager {
constructor(poolConfig) {
this.pool = new ResourcePool(poolConfig);
}
async getResource() {
const resource = await this.pool.acquire();
// Use the async wrapper if your resource cleanup could be async
return new AsyncPooledResource(resource, this.pool);
}
async shutdown() {
await this.pool.close();
}
}
// --- Example Usage ---
// 1. Define how to create and destroy our mock resources
let resourceIdCounter = 0;
const poolConfig = {
create: async () => {
resourceIdCounter++;
console.log(`Creating resource #${resourceIdCounter}...`);
return { id: resourceIdCounter, data: `data for ${resourceIdCounter}` };
},
destroy: async (resource) => {
console.log(`Destroying resource #${resource.id}...`);
},
min: 1,
max: 3
};
// 2. Create the manager
const manager = new ResourceManager(poolConfig);
// 3. Use the pattern in an application function
async function processRequest(requestId) {
console.log(`Request ${requestId}: Attempting to get a resource...`);
try {
await using client = await manager.getResource();
console.log(`Request ${requestId}: Acquired resource #${client.resource.id}. Working...`);
// Simulate some work
await new Promise(resolve => setTimeout(resolve, 500));
// Simulate a random failure
if (Math.random() > 0.7) {
throw new Error(`Request ${requestId}: Simulated random failure!`);
}
console.log(`Request ${requestId}: Work complete.`);
} catch (error) {
console.error(error.message);
}
// `client` is automatically released back to the pool here, in success or failure cases.
}
// --- Simulate concurrent requests ---
async function main() {
const requests = [
processRequest(1),
processRequest(2),
processRequest(3),
processRequest(4),
processRequest(5)
];
await Promise.all(requests);
console.log('\nAll requests finished. Shutting down pool...');
await manager.shutdown();
}
main();
Nếu bạn chạy mã này (sử dụng TypeScript hoặc Babel hiện đại hỗ trợ đề xuất), bạn sẽ thấy các tài nguyên được tạo ra đến giới hạn tối đa, được sử dụng lại bởi các yêu cầu khác nhau và luôn được trả lại cho nhóm. Hàm `processRequest` rõ ràng, tập trung vào nhiệm vụ của nó và hoàn toàn được giải phóng khỏi trách nhiệm dọn dẹp tài nguyên.
Cân Nhắc Nâng Cao và Thông Lệ Tốt Nhất cho Đối Tượng Toàn Cầu
Mặc dù ví dụ của chúng tôi cung cấp một nền tảng vững chắc, nhưng các ứng dụng phân tán trên toàn cầu trong thế giới thực đòi hỏi sự cân nhắc sắc thái hơn.
Đồng Thời và Điều Chỉnh Kích Thước Nhóm
Các kích thước nhóm `min` và `max` là các tham số điều chỉnh quan trọng. Không có một con số kỳ diệu duy nhất; kích thước tối ưu phụ thuộc vào tải của ứng dụng của bạn, độ trễ của việc tạo tài nguyên và giới hạn của dịch vụ phụ trợ (ví dụ: số lượng kết nối tối đa của cơ sở dữ liệu của bạn).
- Quá nhỏ: Các luồng ứng dụng của bạn sẽ dành quá nhiều thời gian để chờ một tài nguyên trở nên khả dụng, tạo ra một nút cổ chai hiệu suất. Điều này được gọi là tranh chấp nhóm.
- Quá lớn: Bạn sẽ tiêu thụ quá nhiều bộ nhớ và CPU trên cả máy chủ ứng dụng và phụ trợ. Đối với một nhóm phân tán trên toàn cầu, điều quan trọng là phải ghi lại lý do đằng sau những con số này, có thể dựa trên kết quả kiểm tra tải, để các kỹ sư ở các khu vực khác nhau hiểu các ràng buộc.
Bắt đầu với các con số bảo thủ dựa trên tải dự kiến và sử dụng các công cụ giám sát hiệu suất ứng dụng (APM) để đo thời gian chờ và mức sử dụng của nhóm. Điều chỉnh cho phù hợp.
Thời Gian Chờ và Xử Lý Lỗi
Điều gì xảy ra nếu nhóm ở kích thước tối đa và tất cả các tài nguyên đang được sử dụng? Nhóm đơn giản của chúng tôi sẽ khiến các yêu cầu mới phải chờ đợi mãi mãi. Một nhóm cấp sản xuất phải có thời gian chờ có được. Nếu một tài nguyên không thể có được trong một khoảng thời gian nhất định (ví dụ: 30 giây), lệnh gọi `acquire` sẽ thất bại với lỗi thời gian chờ. Điều này ngăn các yêu cầu bị treo vô thời hạn và cho phép bạn thất bại một cách duyên dáng, có thể bằng cách trả về trạng thái `503 Service Unavailable` cho máy khách.
Ngoài ra, nhóm nên xử lý các tài nguyên cũ hoặc bị hỏng. Nó nên có một cơ chế xác thực (ví dụ: một hàm `testOnBorrow`) có thể kiểm tra xem một tài nguyên có còn hợp lệ trước khi cho nó mượn hay không. Nếu nó bị hỏng, nhóm nên phá hủy nó và tạo một tài nguyên mới để thay thế nó.
Tích Hợp với Các Khung và Kiến Trúc
Mẫu quản lý tài nguyên này không phải là một kỹ thuật biệt lập; nó là một phần nền tảng của một kiến trúc lớn hơn.
- Dependency Injection (DI): `ResourceManager` mà chúng tôi đã tạo là một ứng cử viên hoàn hảo cho một dịch vụ singleton trong một vùng chứa DI. Thay vì tạo một trình quản lý mới ở mọi nơi, bạn tiêm cùng một phiên bản trên toàn ứng dụng của mình, đảm bảo mọi người chia sẻ cùng một nhóm.
- Microservices: Trong một kiến trúc microservices, mỗi phiên bản dịch vụ sẽ quản lý nhóm kết nối riêng của nó đến cơ sở dữ liệu hoặc các dịch vụ khác. Điều này cô lập các lỗi và cho phép mỗi dịch vụ được điều chỉnh độc lập.
- Serverless (FaaS): Trong các nền tảng như AWS Lambda hoặc Google Cloud Functions, việc quản lý các kết nối nổi tiếng là khó khăn do bản chất vô trạng thái và phù du của các hàm. Một trình quản lý kết nối toàn cục tồn tại giữa các lệnh gọi hàm (sử dụng phạm vi toàn cục bên ngoài trình xử lý) kết hợp với mẫu `using`/nhóm này trong trình xử lý là thông lệ tốt nhất tiêu chuẩn để tránh áp đảo cơ sở dữ liệu của bạn.
Kết Luận: Viết JavaScript Sạch Hơn, An Toàn Hơn và Hiệu Quả Hơn
Quản lý tài nguyên hiệu quả là một dấu hiệu của kỹ thuật phần mềm chuyên nghiệp. Bằng cách vượt ra ngoài mẫu try...finally
thủ công và thường vụng về, chúng ta có thể viết mã linh hoạt hơn, hiệu quả hơn và dễ đọc hơn rất nhiều.
Hãy tóm tắt chiến lược mạnh mẽ mà chúng ta đã khám phá:
- Vấn Đề: Quản lý các tài nguyên bên ngoài đắt tiền, hạn chế như kết nối cơ sở dữ liệu là phức tạp. Dựa vào bộ thu gom rác không phải là một lựa chọn để dọn dẹp tất định và quản lý thủ công với
try...finally
dài dòng và dễ xảy ra lỗi. - Mạng Lưới An Toàn: Cú pháp
using
vàawait using
sắp tới, một phần của đề xuất Quản Lý Tài Nguyên Rõ Ràng TC39, cung cấp một cách khai báo và hầu như không thể sai lầm để đảm bảo rằng logic dọn dẹp luôn được thực thi cho một tài nguyên. - Động Cơ Hiệu Suất: Nhóm tài nguyên là một mẫu đã được thử nghiệm theo thời gian, tránh chi phí cao của việc tạo và phá hủy tài nguyên bằng cách tái sử dụng các tài nguyên hiện có.
- Tổng Hợp: Bằng cách tạo một trình bao bọc triển khai giao thức dispose (
[Symbol.dispose]
hoặc[Symbol.asyncDispose]
) và có logic dọn dẹp là giải phóng một tài nguyên trở lại nhóm của nó, chúng ta đạt được điều tốt nhất của cả hai thế giới. Chúng ta có được hiệu suất của nhóm với sự an toàn và thanh lịch của câu lệnhusing
.
Khi JavaScript tiếp tục trưởng thành như một ngôn ngữ hàng đầu để xây dựng các hệ thống quy mô lớn, hiệu suất cao, việc áp dụng các mẫu như thế này không còn là tùy chọn. Đó là cách chúng ta xây dựng thế hệ ứng dụng mạnh mẽ, có khả năng mở rộng và dễ bảo trì tiếp theo cho đối tượng toàn cầu. Bắt đầu thử nghiệm với khai báo using
trong các dự án của bạn ngay hôm nay thông qua TypeScript hoặc Babel và kiến trúc quản lý tài nguyên của bạn một cách rõ ràng và tự tin.