Skip to content

Latest commit

 

History

History
513 lines (379 loc) · 17.8 KB

readme.ar.md

File metadata and controls

513 lines (379 loc) · 17.8 KB

مـؤجلات (Promises)

[English]

مكتبة بسيطة لدعم المؤجلات (promises) في لغة الأسس.

توفر هذه المكتبة الدعم لمختلف وظائف المؤجلات بشكل مستقل عن المكان الذي سيتم استعمالها فيه. لا تقوم هذه المكتبة بتوفير حلقة أحداث (event loop)، حيث يجب على المكان الذي سيتم استعمالها فيه توفير ذلك بشكل متوافق مع هذه المكتبة.

الإضافة إلى المشروع

يمكن تثبيت هذه المكتبة باستعمال التعليمات البرمجية التالية:

اشمل "مـحا"؛
مـحا.اشمل_ملف("Alusus/Promises"، "مـؤجلات.أسس")؛
import "Apm";
Apm.importFile("Alusus/Promises");

مثال

اشمل "مـتم/طـرفية"؛
اشمل "مـتم/نـص"؛
اشمل "مـتم/مـصفوفة"؛
اشمل "مـتم/سندات"؛
اشمل "مـحا"؛
مـحا.اشمل_ملف("Alusus/Promises"، "مـؤجلات.أسس")؛

استخدم مـتم؛
استخدم مـؤجلات؛

// نقوم بتعريف صف يمثل رقم، لاستعماله في الأمثلة التالية
صنف رقـم {
    عرف القيمة: صـحيح؛
    عملية هذا~هيئ() هذا.القيمة = 0؛
    عملية هذا~هيئ(i: صـحيح) هذا.القيمة = i؛
    عملية هذا~هيئ(i: سند[رقـم]) هذا.القيمة = i.القيمة؛
    عملية هذا = سند[رقـم] هذا.القيمة = value.القيمة؛
}

// اختبر مـؤجلة.
دالة اختبر_مؤجلة {
    طـرفية.اطبع("اختبر مـؤجلة:\ج")؛
    // نقوم بإنشاء مؤجلة نمط القيمة المرجعة لها هو Num
    عرف مؤجلة: سـندنا[مـؤجلة[رقـم]] = مـؤجلة[رقـم].أنشئ()؛
    طـرفية.اطبع("مؤجلة 1 - الحالة: %d، النتيجة: %d\ج"، مؤجلة.الحالة، مؤجلة.النتيجة.القيمة)؛

    // نقوم بإقرار المؤجلة بحيث تكون النتيجة هي كائن من الصف Num يحمل القيمة 5
    مؤجلة.قرر(رقـم(5))؛
    طـرفية.اطبع("مؤجلة 1 - الحالة: %d، النتيجة: %d\ج"، مؤجلة.الحالة، مؤجلة.النتيجة.القيمة)؛

    // نقوم برفض المؤجلة مع خطأ له الرمز 1 و رسالة مناسبة
    مؤجلة.ارفض(مثل_سندنا[سـندنا[خـطأ_عام]().{
        أنشئ()؛
        الرمز = "مثال_خطأ1"؛
        الرسالة = نـص("خطأ غير معروف 1")؛
    }، خـطأ])؛
    طـرفية.اطبع("مؤجلة 1 - الحالة: %d، الخطأ: %ld\ج"، مؤجلة.الحالة، مؤجلة.الخطأ.obj~ptr)؛
}
اختبر_مؤجلة()؛

// اختبر مـؤجلة.ثم
دالة اختبر_مؤجلة_ثم {
    طـرفية.اطبع("\جاختبر مـؤجلة.ثم:\ج")؛
    // Num نقوم بإنشاء مؤجلة نمط القيمة المرجعة لها هو
    عرف مؤجلة: سـندنا[مـؤجلة[رقـم]] = مـؤجلة[رقـم].أنشئ()؛

    // نقوم بتحديد ما يجب تنفيذه بعد تنفيذ المؤجلة
    // هنا ما سيتم تنفيذه هو طباعة رسالة و إقرار المؤجلة
    عرف ثم: سـندنا[مـؤجلة[نـص]] = مؤجلة.ثم[نـص](
        مغلفة (مدخل: رقـم، م: سند[مـؤجلة[نـص]]) {
            طـرفية.اطبع("أطلقت مـؤجلة.ثم\ج")؛
            م.قرر(نـص("مـؤجلة.ثم - النتيجة المستلمة: ") + مدخل.القيمة)؛
        }
    )؛
    طـرفية.اطبع("الحالة: %d، النتيجة: %s\ج"، ثم.الحالة، ثم.النتيجة.صوان)؛
    مؤجلة.قرر(رقـم(6))؛
    طـرفية.اطبع("الحالة: %d، النتيجة: %s\ج"، ثم.الحالة، ثم.النتيجة.صوان)؛
}
اختبر_مؤجلة_ثم()؛
import "Srl/Console";
import "Srl/errors";
import "Apm";
Apm.importFile("Alusus/Promises");

use Srl;
use Promises;

// نقوم بتعريف صف يمثل رقم، لاستعماله في الأمثلة التالية
class Num {
    def val: Int;
    handler this~init() this.val = 0;
    handler this~init(i: Int) this.val = i;
    handler this~init(i: ref[Num]) this.val = i.val;
    handler this = ref[Num] this.val = value.val;
}


func testPromise {
    Console.print("Test Promise:\n");
    // Num نقوم بإنشاء مؤجلة نمط القيمة المرجعة لها هو
    def promise: SrdRef[Promise[Num]] = Promise[Num].new();
    Console.print("promise 1 - status: %d, value: %d\n", promise.status, promise.result.val);

    //يحمل القيمة 5 Num  نقوم بإقرار المؤجلة بحيث تكون النتيجة هي كائن من الصف
    promise.resolve(Num(5));
    Console.print("promise 1 - status: %d, value: %d\n", promise.status, promise.result.val);

    // نقوم برفض المؤجلة مع خطأ له الرمز 1 و رسالة مناسبة
    promise.reject(castSrdRef[SrdRef[GenericError]().{
        construct();
        code = "example_err1";
        message = String("Unknown error 1");
    }, Error]);
    Console.print("promise 1 - status: %d, error: %ld\n", promise.status, promise.error.obj~ptr);
}
testPromise();


func testPromiseThen {
    Console.print("\nTest Promise.then:\n");
    // Num نقوم بإنشاء مؤجلة نمط القيمة المرجعة لها هو
    def promise: SrdRef[Promise[Num]] = Promise[Num].new();

    // نقوم بتحديد ما يجب تنفيذه بعد تنفيذ المؤجلة
    // هنا ما سيتم تنفيذه هو طباعة رسالة و إقرار المؤجلة
    def then: SrdRef[Promise[String]] = promise.then[String](
        closure (input: Num, p: ref[Promise[String]]) {
            Console.print("ThenPromise triggered\n");
            p.resolve(String("ThenPromise - received value: ") + input.val);
        }
    );
    Console.print("status: %d, value: %s\n", then.status, then.result.buf);
    promise.resolve(Num(6));
    Console.print("status: %d, value: %s\n", then.status, then.result.buf);
}
testPromiseThen();

الأصناف والدالات

الصنف مـؤجلة (Promise)

صنف مـؤجلة [نـوع_الـنتيجة: نوع]{
    عرف الحالة: صـحيح = حـالة._جديد_؛
    عرف النتيجة: نـوع_الـنتيجة؛
    عرف الخطأ: سـندنا[خـطأ]؛
}
class Promise [ResultType: type] {
    def status: Int = Status.NEW;
    def result: ResultType;
    def error: SrdRef[Error];
}

هذا الصنف عبارة عن قالب لتخزين معلومات المؤجلة لمختلف أنواع النتائج.

الحالة (status) يعبر عن الحالة الحالية للمؤجلة.

النتيجة (result) النتيجة المرجعة من المؤجلة.

الخطأ (error) الخطأ الذي حصل أثناء محاولة تنفيذ المؤجلة.

قرر (resolve)

عرف قرر: (نتيجة: نـوع_الـنتيجة)؛
handler this.resolve(res: ResultType);

وظيفة يمكن عن طريقها اقرار المؤجلة بتحويل حالتها من __جديد__ إلى __مقرر__ و تخزين النتيجة.

المعطيات:

نتيجة (res) النتيجة التي يجب تخزينها في المؤجلة على أنها نتيجة التنفيذ.

عرف قرر: (م: سـندنا[مـؤجلة[نـوع_الـنتيجة]])؛
handler this.resolve(p: SrdRef[Promise[ResultType]]);

قرر نتيجة المؤجلة الحالية باستخدام مؤجلة أخرى. المؤجلة الحالية ستنتظر انتهاء المؤجلة المعطاة وتستلم نتيجتها سواء كانت مقررة أم مرفوضة.

م (p) المؤجلة المرجو انتظارها واستلام النتيجة منها.

ارفض (reject)

عرف ارفض: (خطأ: سندنا[خـطأ])؛
handler this.reject(err: SrdRef[Error]);

وظيفة يمكن عن طريقها رفض المؤجلة بتحويل حالتها من __جديد__ إلى __مرفوض__ و تخزين الخطأ الذي حصل.

المعطيات:

خطأ (error) الخطأ الذي يجب تخزينه في المؤجلة على أنه الخطأ الحاصل أثناء التنفيذ.

أنشئ (new)

عرف ارفض: (): سندنا[مـؤجلة[نـوع_الـنمط]]؛
function new (): SrdRef[Promise[ResultType]];

قالب وظيفة يمكن عن طريقه إنشاء مؤجلة بنمط قيمة مرجعة محدد.

القيمة المرجعة:

سند إلى مؤجلة من النوع المطلوب.

ثم (then)

عرف [نـوع_ثـم: نوع] ثم(
    منادى: دالةـمغلفة(دخل: نـوع_الـنتيجة، مـؤجلة: سند[مـؤجلة[نـوع_ثـم]])
): سندنا[مـؤجلة[نـوع_ثـم]]؛
handler [ThenType: type] this.then(
    callback: closure (input: ResultType, promise: ref[Promise[ThenType]])
): SrdRef[Promise[ThenType]];

قالب وظيفة يمكن من خلاله تحديد ما يجب تنفيذه بعد أن يتم الإنتهاء من تنفيذ مؤجلة.

المعطيات:

منادى (callback) عبارة عن دالة مغلفة تحدد ما الذي يجب تنفيذه كنتيجة للمؤجلة.

القيمة المرجعة:

سند لمؤجلة نمط النتيجة الخاصة بها يحدده القالب.

اقبض (catch)

عرف اقبض(
    منادى: دالةـمغلفة(خطأ: سندنا[خـطأ]، مـؤجلة: سند[مـؤجلة[نـوع_الـنتيجة]])
): سندنا[مـؤجلة[نـوع_الـنتيجة]]؛
handler this.catch(
    callback: closure (err: SrdRef[Error], promise: ref[Promise[ResultType]])
): SrdRef[Promise[ResultType]];

وظيفة يمكن من خلالها تحديد ما يجب تنفيذه عند حدوث خطأ أثناء تنفيذ مؤجلة.

المعطيات:

منادى (callback) عبارة عن دالة مغلفة تحدد ما الذي يجب تنفيذه عند حدوث الخطأ.

القيمة المرجعة:

سند لمؤجلة نمط النتيجة الخاصة بها يحدده القالب.

الكل (all)

عرف الكل (
    الدخل: مـصفوفة[سندنا[مـؤجلة[نـوع_الـنتيجة]]]
): سندنا[مـؤجلة[نـوع_الـنتيجة]]؛
function all (inputs: Array[SrdRef[Promise[ResultType]]]): SrdRef[Promise[Array[ResultType]]];

دالة يمكن من خلالها تحديد مجموعة من المؤجلات، بحيث تكون المؤجلة المرجعة محققة في حال تحققت كافة المؤجلات، و غير محققة في حال كانت واحدة على الأقل من المؤجلات غير محققة..

المعطيات:

الدخل (inputs) مجموعة المؤجلات التي تعتمد عليها المؤجلة التي يتم إرجاعها.

القيمة المرجعة:

سند لمؤجلة نمط النتيجة الخاصة بها يحدده القالب.

تجاهل_النتيجة (ignoreResult)

عرف تجاهل_النتيجة: (): سندنا[مـؤجلة[صـحيح]]؛
handler this.ignoreResult(): SrdRef[Promise[Int]];

وظيفة يمكن من خلالها إهمال نتيجة مؤجلة.

يمكن استعمال هذه الوظيفة لإهمال النتيجة في حال استعمال دالة الكل لمجموعة من المؤجلات بأصناف مختلفة. حيث أن دالة الكل تحتاج مجموعة من المؤجلات لها نفس الصنف و إلا يعطي المترجم خطأ.

القيمة المرجعة:

سند إلى مؤجلة لها قيمة معادة Int، و تكون قيمة النتيجة هي 0 للدلالة على أنها مهملة.

حـالة (Status)

صنف حـالة {
    عرف _جديد_: 0؛
    عرف _مقرر_: 1؛
    عرف _مرفوض_: 2؛
}
def Status: {
    def NEW: 0;
    def RESOLVED: 1;
    def REJECTED: 2;
}

يستعمل هذا الصنف لتحديد حالة المؤجلة.

المؤجلات العودية (recursive)

في بعض الحالات تحتاج إنشاء مؤجلات عودية تستمر في حلقة طويلة جدا أو لا متناهية. مثال ذلك المؤجلات التي تقرأ بيانات من الشبكة بشكل دوري أو المؤجلات التي تعمل بشكل متتالي مع انتظار ما بين تشغيلة وأخرى أو المؤجلات التي تواجه خطأ في قراءة بيانات من الخادم وترغب بإعادة محاولة العملية بشكل لا متناهي لحين استعادة الخادم عافيته. في مثل هذه الحالات يكون استخدام العملية قرر (resolve) مع إعطائها مؤجلة جديدة خطرًا لأنه قد يتسبب في تجاوز حدود المكدس (stack overflow) بالإضافة لاستخدام حيز أكبر من الذاكرة مما يجب.

لحل هذه المشكلة توفر مكتبة المؤجلات الدالة أعد (retry). هذه الدالة مشابهة لدالة قرر لكنها تعيد استخدام المؤجلة الحالية وبالتالي تتجنب تضخيم استخدام المكدس أو الذاكرة لأنها تستبدل المؤجلة المعتمدة بأخرى جديدة. لتوضيح الصورة لنفترض أن عندك مؤجلة م1 مقام عليها عملية ثم. عملية ثم تنتج كائن ث1 يعتمد على م1. إذا أردت داخل ث1 أن تعيد العملية فتنتج م2 وتطبق عليه ث2 وتستخدم ث2 و م2 كمعطى لدالة قرر وهكذا. بعد خمس عودات فإن التسلسل الزمني لتغير الاعتمادات بين الكائنات يكون بالشكل التالي:

  • ث1 -> م1
  • ث1 -> ث2 -> م2
  • ث1 -> ث2 -> ث3 -> م3
  • ث1 -> ث2 -> ث3 -> ث4 -> م4
  • ث1 -> ث2 -> ث3 -> ث4 -> ث5 -> م5

أما في حالة استخدام عملية أعد فأنت لست بحاجة لإعادة تطبيق عملية ثم على المؤجلة الجديدة وإنما تمررها لعملية ثم الحالية لتعيد استخدامها، ما ينتج عنه التسلسل الزمني التالي:

  • ث1 -> م1
  • ث1 -> م2
  • ث1 -> م3
  • ث1 -> م4
  • ث1 -> م5

عملية أعد يمكن استخدامها في عمليتي ثم و اقبض:

مـؤجلة_ثم (ThenPromise)

كي تستخدم العملية أعد داخل مغلفة ثم تحتاج لتغيير تعريف المعطى الثاني من مـؤجلة (Promise) إلى مـؤجلة_ثم (ThenPromise) ثم استخدام الدالة أعد (retry) على المؤجلة، كما في المثال التالي:

عرف مؤجلة: سـندنا[مـؤجلة[رقـم]] = مـؤجلة[رقـم].أنشئ()؛
عرف ثم: سـندنا[مـؤجلة[نـص]] = مؤجلة.ثم[نـص](
    مغلفة (مدخل: رقـم، م: سند[مـؤجلة_ثم[نـص، رقـم]]) {
        طـرفية.اطبع("أطلقت مـؤجلة.ثم %d\ج"، مدخل.القيمة)؛
        مؤجلة = مـؤجلة[رقـم].أنشئ()؛
        م.أعد(مؤجلة)؛
    }
)؛
عرف ع: صـحيح؛
لكل ع = 0، ع < 10، ++ع {
    مؤجلة.قرر(رقـم(ع))؛
}
def promise: SrdRef[Promise[Num]] = Promise[Num].new();
def then: SrdRef[Promise[String]] = promise.then[String](
    closure (input: Num, p: ref[ThenPromise[String, Num]]) {
        Console.print("ThenPromise triggered %d\n", input.val);
        promise = Promise[Num].new();
        p.retry(promise);
    }
);
def i: Int;
for i = 0, i < 10, ++i {
    promise.resolve(Num(i));
}

مـؤجلة_قبض (CatchPromise)

كي تستخدم العملية أعد داخل مغلفة اقبض تحتاج لتغيير تعريف المعطى الثاني من مـؤجلة (Promise) إلى مـؤجلة_قبض (CatchPromise) ثم استخدام الدالة أعد (retry) على المؤجلة، كما في المثال التالي:

عرف مؤجلة: سـندنا[مـؤجلة[رقـم]] = مـؤجلة[رقـم].أنشئ()؛
عرف اقبض: سـندنا[مـؤجلة[رقـم]] = مؤجلة.اقبض(
    مغلفة (خطأ: سـندنا[خـطأ]، م: سند[مـؤجلة_قبض[رقـم]]) {
        طـرفية.اطبع("أطلقت مـؤجلة_قبض. الخطأ: %s\ج"، خطأ.هات_الرسالة().صوان)؛
        مؤجلة = مـؤجلة[رقـم].أنشئ()؛
        م.أعد(مؤجلة)؛
    }
)؛
عرف ع: صـحيح؛
لكل ع = 0، ع < 10، ++ع {
    مؤجلة.ارفض(مثل_سندنا[سـندنا[خـطأ_عام]().{
        أنشئ()؛
        الرمز = نـص("مثال_خطأ") + ع؛
        الرسالة = نـص("خطأ غير معروف ") + ع؛

    }، خـطأ])؛
}
def promise: SrdRef[Promise[Num]] = Promise[Num].new();
def catch: SrdRef[Promise[Num]] = promise.catch(
    closure (err: SrdRef[Error], p: ref[CatchPromise[Num]]) {
        Console.print("CatchPromise triggered. error: %s\n", err.getMessage().buf);
        promise = Promise[Num].new();
        p.retry(promise);
    }
);
def i: Int;
for i = 0, i < 10, ++i {
    promise.reject(castSrdRef[SrdRef[GenericError]().{
        construct();
        code = String("example_err") + i;
        message = String("Unknown error ") + i;
    }, Error]);
}