سلسلة Swift Concurrency 02 | تم حل المشكلات عن طريق المزامنة/الانتظار
الهدف هو جعل النظام غير المتزامن يستعيد شروط الصيانة طويلة المدى
إذا نظرت إلى السطح فقط، يبدو أن async/await يحل ثلاث مشكلات بشكل أساسي:
- عدد كبير جدًا من عمليات الاسترجاعات
- التعشيش عميق جداً
- الكود قبيح جدًا
ولكن بعد استخدامه فعليًا في المشروع لفترة من الوقت، ستجد أن هذه مجرد مظاهر. ما يحله حقًا هو: ** يمكن أخيرًا تنظيم الأعمال غير المتزامنة وتفسيرها وإعادة استخدامها مثل الأعمال العادية. **
هذا تغيير في قابلية الصيانة الهندسية.
1. أول شيء يحله هو: لم تعد أساليب الكتابة تُختطف العمليات التجارية
العديد من الشركات غير المتزامنة في حد ذاتها ليست معقدة. ما هو معقد هو أن طريقة الكتابة القديمة تجعل الأمر معقدًا.
على سبيل المثال، عملية حقيقية ولكنها شائعة:
- سحب معلومات المستخدم
- التحقق من حالة العضوية
- اسحب المحتوى الموصى به إلى الصفحة الرئيسية
- إذا فشلت إحدى الخطوات، فارجع إلى الشاشة الكاملة
- آخر تحديث لحالة الصفحة
كان من الممكن أن تكون هذه عملية تسلسلية واضحة للغاية.
لكن في عصر الاكتمال، غالبًا ما يتعين علينا التعامل مع “قضايا الكتابة” هذه أولاً:
- أين يتم كتابة الفرع الناجح؟
- أين يتم كتابة الفرع الفاشل؟
- أين يتم كتابة تبديل الخيط الرئيسي؟
- ما هي الطبقة المسؤولة عن العودة المبكرة؟
- ما هي الطبقة المسؤولة عن توحيد أخطاء القنبلة؟
ثم لم يعد التعقيد يأتي من العمل، ولكن من التعبير نفسه.
async/await الفائدة الأكثر مباشرة هي إزالة هذه الطبقة الإضافية من الضوضاء وإعادة تعقيد الأعمال إلى الشركة نفسها.
2. يجعل “التبعية” واضحة مرة أخرى
العديد من العمليات غير المتزامنة تعتمد بقوة.
على سبيل المثال:
- فقط بعد الحصول على حالة تسجيل الدخول يمكنك الحصول على معلومات المستخدم
- فقط بعد الحصول على معلومات المستخدم يمكنك تحديد الوحدات التي سيتم تحميلها على الصفحة الرئيسية.
- فقط بعد الحصول على التكوين التجريبي يمكنك تحديد مسار عرض الصفحة
بالطبع، يمكن أن يعبر الإكمال أيضًا عن هذه العلاقات، لكن المشكلة هي: أنه غالبًا ما يخفي التبعيات في طبقات التداخل. يمكن تشغيل التعليمات البرمجية، لكن سلسلة التبعية لم تعد واضحة.
إحدى القيم الأساسية لـ async/await هي إعادة علاقة “من يعتمد على من” إلى تدفق التحكم الخطي:
let session = try await authService.loadSession()
let user = try await userService.loadUser(session: session)
let modules = try await homeService.loadModules(for: user)
لا تقتصر قيمة هذا الرمز على مظهره الجيد فحسب، بل إنه يمكن رؤيته على الفور:
- العلاقة التسلسلية
- نقطة الاستراحة
- ما هي الخطوة التي قد تكون خاطئة؟
وهذا سيؤثر بشكل مباشر على ثقتك بنفسك عند تعديل المتطلبات لاحقًا، لأنه يمكنك أخيرًا تحديد مكان القسم الذي تنقله في الرابط بأكمله.
3. يعمل على تحسين انتشار الأخطاء بشكل ملحوظ
في النموذج القديم غير المتزامن، كانت معالجة الأخطاء هي الأكثر هشاشة.
نظرًا لأن كل مستوى من مستويات الإكمال قد يفشل، فغالبًا ما ينتهي الأمر بالحالة التالية:
- مجموعة من التعامل مع فشل طلب المستوى الأول
- مجموعة أخرى من العلاجات لفشل الطبقة الثانية
- إذا فشل المستوى الثالث، حاول مستوى آخر.
- بعض الأماكن ابتلعتها بشكل خاطئ
- لعب نخب في بعض الأماكن
- بعض الأماكن سجل فقط
ستجد أن المشكلة الحقيقية هي أن مسار الخطأ مقسم مثل العملية الرئيسية.
async/await يعطي على الأقل نموذجًا موحدًا لنشر الأخطاء:
- التخلص من هذه الطبقة
- فقط استمر في الرمي للأعلى
- مسؤولية موحدة من المستويات العليا
بمعنى آخر، فهو لا يقرر للفريق “كيفية التعامل مع الأخطاء”، ولكنه على الأقل يجعل “كيفية تدفق الأخطاء” واضحة مرة أخرى. هذا مهم بشكل خاص لحدود الأعمال.
4. يعرض بوضوح نقطة الإيقاف المؤقت
من المواقف الشائعة تجاهل المعنى الأكثر أهمية لـ await:
إنها تذكير.
إنه يقول:
- ممكن وقفة هنا
- قد يختلف السياق هنا
- لا ينبغي أن يكون الكود الموجود بعد ذلك في نفس البيئة بشكل افتراضي
هذا هو مفتاح التفكير المتزامن.
لأن الخطر الحقيقي للتعليمات البرمجية غير المتزامنة هو أنه بعد استدعائها، غالبًا ما تفكر دون وعي:
- الصفحة الحالية لا تزال هناك
- الوضع الحالي لم يتغير
- يبقى الكائن الحالي سليما
وawait على الأقل يشير بشكل واضح إلى “هذه هي الحدود” نحويًا.
سيجبرك هذا على البدء في التفكير بجدية في صلاحية الحالة، بدلاً من التعامل مع غير المتزامن باعتباره استدعاء دالة عادية.
5. يسهل الدخول إلى التصميم الرئيسي بدلاً من منطق المكونات الإضافية.
في الماضي، كان بإمكان العديد من الرموز أيضًا إجراء الإلغاء، ولكن الإلغاء كان عادةً بمثابة وظيفة اللحاق بالركب:
- حفظ رمز مميز بشكل منفصل
- تذكر أن تقوم بالإلغاء عند إتلاف الصفحة
- إيقاف الطلبات القديمة يدويًا في بعض السيناريوهات
هل يمكن القيام بذلك؟ بالطبع يمكنك. لكن المشكلة هي أن الإلغاء في النموذج القديم غالبًا لا يكون جزءًا من العملية الرئيسية، بل مثل التصحيح الخارجي.
في Swift Concurrency، يُنظر أخيرًا إلى المهام والإلغاء بشكل طبيعي على أنها مجموعة من الأشياء. وهذا يفرض عدة أسئلة رئيسية يجب طرحها في وقت سابق:
- من يملك هذه المهمة؟
- هل يجب أن يتوقف عند ترك الصفحة؟
- المهام الجديدة قادمة، هل يجب أن تصبح المهام القديمة غير صالحة؟
لا يساعد ذلك تلقائيًا في الحصول على الإلغاء بشكل صحيح، ولكنه يبرز حقيقة أن الإلغاء يجب أن يكون مصممًا فيه.
6. إنه لا يحسن الخبرة الشخصية فحسب، بل يحسن أيضًا اتساق تعاون الفريق.
هذا هو الوضع الشائع الذي يتم الاستهانة به.
عندما يكتب شخص ما رمزًا، فإن الفرق بين الاكتمال وasync/await قد ينعكس فقط في السلاسة. ولكن في مشاريع التعاون متعددة الأشخاص، الشيء المهم حقًا في async/await هو أنه يجعل تعبير الفريق أكثر توحيدًا.
الآن على الأقل من الممكن أن يكون الوضع الافتراضي هو:
- ستظهر القدرات غير المتزامنة في وظيفة
async - فشل مع
throw - استخدم
awaitلنقطة الإيقاف المؤقت
هذا التوحيد مهم جدا. لأن الخوف الأكبر في المشاريع المعقدة هو أن كل شخص يستخدم طريقة التنظيم غير المتزامنة الخاصة به. عاجلاً أم آجلاً، سيصبح المشروع قابلاً للتشغيل، لكن لن يتمكن أحد من توليه بسرعة.
7. لا يحل أي شيء
في هذه المرحلة، يجب أيضًا أن نوضح أن async/await لا يحل هذه المشكلات:
- لا يزيل ظروف السباق تلقائيًا
- لا يمنع الطلبات القديمة تلقائيًا من استبدال النتائج الجديدة
- لن يقرر للفريق كيفية رسم حدود الحالة
- لا يضمن تلقائيًا حدوث تحديثات واجهة المستخدم في السياق الصحيح
لذا، إذا كان التصميم الأصلي غير المتزامن للمشروع فوضويًا للغاية، فقد يكون “من الأفضل أن يبدو فوضويًا” بعد استبداله بـ async/await.
ويجب رؤية هذا بوضوح.
async/await ليس حلاً سحريًا معماريًا، ولكنه يسلط الضوء فقط على العديد من المشكلات التي تم تغطيتها في الأصل بالكتابة.
8. الاستنتاج: ما تم حله هو “هل يمكن الحفاظ على التعليمات البرمجية غير المتزامنة لفترة طويلة؟”
ولكي أختصر الأمر أقول:
async/awaitالحل الحقيقي هو أن التعليمات البرمجية غير المتزامنة تصبح صعبة التنظيم والتفكير والصيانة في المشاريع المعقدة.
لذا فإن العبارة الأكثر دقة ليست:
“إنه يجعل الكود أقصر.”
بدلاً من ذلك:
“إنه يعيد التعليمات البرمجية غير المتزامنة إلى الحالة الهيكلية من أجل قابلية الصيانة على المدى الطويل.”
What to read next
Want more posts about Swift Concurrency?
Posts in the same category are usually the best next step for reading more on this topic.
View same categoryWant to keep following #iOS?
Tags are useful for related tools, specific problems, and similar troubleshooting notes.
View same tagWant to explore another direction?
If you are not sure what to read next, return to the homepage and start from categories, topics, or latest updates.
Back home