الكلمة المفتاحية using في TypeScript والإدارة الصريحة للموارد
غالبًا ما تنبع تسريبات الذاكرة (Memory leaks) في بيئة الإنتاج من خطأ واحد: يقوم المطورون بالحصول على الموارد ولكنهم يفشلون في تحريرها.
تظل اتصالات قواعد البيانات مفتوحة بعد حدوث الأخطاء. وتستهلك مقابض الملفات (File handles) موارد النظام. وتظل عملاء WebSocket متصلين بخوادم متوقفة. يحدث هذا عندما تعتمد على التنظيف اليدوي داخل كتل finally.
تحل الكلمة المفتاحية using في TypeScript هذه المشكلة. وهي جزء من مقترح ECMAScript للإدارة الصريحة للموارد (Explicit Resource Management). فهي تضمن التنظيف من خلال نمط "القابل للتخلص" (disposable pattern). عندما يخرج مورد ما عن نطاقه (scope)، يقوم TypeScript بتشغيل طريقة التخلص منه تلقائيًا.
لن تحتاج بعد الآن إلى كتل finally اليدوية. لن تنسى التنظيف بعد الآن. ولن تتسبب في تسريب الاتصالات بعد الآن.
كيف تعمل:
- تضمن الكلمة المفتاحية
usingالتخلص من الموارد عند خروجها من النطاق. - تقوم الموارد القابلة للتخلص بتنفيذ
Symbol.disposeللمهام المتزامنة (sync) أوSymbol.asyncDisposeللمهام غير المتزامنة (async). - يحول TypeScript تصريحات
usingإلى كتلtry-finallyمع مكدس تخلص تلقائي (automatic disposal stack). - يمنع هذا النمط التسريبات الناتجة عن عمليات الإرجاع المبكرة (early returns)، أو الاستثناءات (exceptions) الملقاة، أو الكود المنسي.
استخدم using لأي مورد له عمر محدد، مثل اتصالات قواعد البيانات، أو مقابض الملفات، أو الأقفال (locks)، أو المؤقتات (timers).
تستخدم هذه الآلية بروتوكول التخلص. حيث تقوم الكائنات بتنفيذ طريقة مفتاحها هو Symbol.dispose. وعندما يخرج النطاق عبر الإكمال الطبيعي، أو الإرجاع (return)، أو استثناء، يقوم TypeScript باستدعاء تلك الطريقة.
مثال على مقبض ملف:
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);
}
يتم تشغيل عملية التخلص في كتلة finally يتم إنشاؤها تلقائيًا. وهي تعمل حتى لو قامت الدالة برمي خطأ أو أرجعت قيمة مبكرًا.
يحافظ TypeScript على مكدس تخلص (disposal stack). حيث يقوم بالتخلص من الموارد بترتيب عكسي لطريقة الحصول عليها. وهذا يتوافق مع ترتيب التبعية الطبيعي لمعظم منطق التنظيف.
للتنظيف غير المتزامن، استخدم await using. يتطلب هذا أن يقوم المورد بتنفيذ Symbol.asyncDispose. وسينتظر TypeScript الوعد (Promise) المُرجع قبل الاستمرار.
ثلاث قواعد للنجاح:
- لا تقم برمي استثناءات داخل طرق التخلص الخاصة بك. بدلاً من ذلك، قم بالإمساك بالأخطاء وتسجيلها داخليًا.
- لا تستخدم العمليات غير المتزامنة داخل طريقة
Symbol.disposeالمتزامنة. استخدمSymbol.asyncDisposeبدلاً من ذلك. - تذكر أن تصريحات
usingمحددة بنطاق الكتلة (block-scoped). المورد الموجود داخل كتلةifيتم التخلص منه عند انتهاء تلك الكتلة، وليس عند انتهاء الدالة.
يوفر هذا النمط الأمان مع تكلفة تشغيلية ضئيلة للغاية.
المصدر: https://dev.to/jsmanifest/typescript-using-keyword-and-explicit-resource-management-done-right-22pg
