You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Copy file name to clipboardExpand all lines: src/ar/index.md
+84-13Lines changed: 84 additions & 13 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -468,9 +468,11 @@ class ViewModel {
468
468
```
469
469
470
470
<divclass="warning">
471
-
<h4>Task.detached عادةً خاطئ</h4>
471
+
<h4>Task و Task.detached نمط سيء</h4>
472
472
473
-
فريق Swift يوصي بـ [Task.detached كملاذ أخير](https://forums.swift.org/t/revisiting-when-to-use-task-detached/57929). لا ترث الأولوية، القيم المحلية للمهمة، أو سياق الـ actor. معظم الوقت، `Task` العادية هي ما تريده. إذا كنت تحتاج عملاً مكثفاً على المعالج بعيداً عن الـ main actor، ضع علامة `@concurrent` على الدالة بدلاً من ذلك.
473
+
المهام التي تجدولها بـ `Task { ... }` غير مُدارة. لا توجد طريقة لإلغائها أو معرفة متى تنتهي، إن انتهت أصلاً. لا توجد طريقة للوصول لقيمة الإرجاع أو معرفة إذا واجهت خطأ. في غالبية الحالات، سيكون من الأفضل استخدام مهام مُدارة بواسطة `.task` أو `TaskGroup`، [كما هو موضح في قسم "الأخطاء الشائعة"](#managedtasks).
474
+
475
+
[Task.detached يجب أن يكون ملاذك الأخير](https://forums.swift.org/t/revisiting-when-to-use-task-detached/57929). المهام المنفصلة لا ترث الأولوية، القيم المحلية للمهمة، أو سياق الـ actor. إذا كنت تحتاج عملاً مكثفاً على المعالج بعيداً عن الـ main actor، ضع علامة `@concurrent` على الدالة بدلاً من ذلك.
474
476
</div>
475
477
476
478
<divclass="analogy">
@@ -609,24 +611,93 @@ func badIdea() async {
609
611
610
612
تجمع الخيوط التعاوني في Swift له خيوط محدودة. حجب واحد بـ `DispatchSemaphore` أو `DispatchGroup.wait()` أو استدعاءات مماثلة يمكن أن يسبب جموداً. إذا كنت تحتاج ربط كود متزامن وغير متزامن، استخدم `async let` أو أعد الهيكلة للبقاء غير متزامن بالكامل.
611
613
612
-
### إنشاء Tasks غير ضرورية
614
+
<divid="managedtasks">
615
+
616
+
### إنشاء مهام غير مُدارة
617
+
618
+
المهام التي تنشئها يدوياً بـ `Task { ... }` أو `Task.detached { ... }` غير مُدارة. بعد إنشاء مهام غير مُدارة، لا يمكنك التحكم بها. لا يمكنك إلغاؤها إذا تم إلغاء المهمة التي بدأتها منها. لا يمكنك معرفة إذا أنهت عملها، إذا ألقت خطأ، أو جمع قيمة الإرجاع. بدء مثل هذه المهمة يشبه رمي زجاجة في البحر آملاً أن توصل رسالتها لوجهتها، دون رؤية تلك الزجاجة مرة أخرى.
619
+
620
+
<divclass="analogy">
621
+
<h4>مبنى المكاتب</h4>
622
+
623
+
`Task` يشبه تكليف موظف بعمل. الموظف يتعامل مع الطلب (بما في ذلك الانتظار لمكاتب أخرى) بينما تستمر في عملك الفوري.
624
+
625
+
بعد إرسال العمل للموظف، ليس لديك وسيلة للتواصل معها. لا يمكنك إخبارها بالتوقف عن العمل أو معرفة إذا انتهت وما كانت نتيجة ذلك العمل.
626
+
627
+
ما تريده فعلاً هو إعطاء الموظف جهاز اتصال لاسلكي للتواصل معها أثناء تعاملها مع الطلب. بجهاز الاتصال، يمكنك إخبارها بالتوقف، أو يمكنها إخبارك عندما تواجه خطأ، أو يمكنها الإبلاغ عن نتيجة الطلب الذي أعطيتها إياه.
628
+
</div>
629
+
630
+
بدلاً من إنشاء مهام غير مُدارة، استخدم تزامن Swift للحفاظ على التحكم في المهام الفرعية التي تنشئها. استخدم `TaskGroup` لإدارة (مجموعة من) المهام الفرعية. Swift توفر عدة دوال `withTaskGroup() { group in ... }` للمساعدة في إنشاء مجموعات المهام.
613
631
614
632
```swift
615
-
// إنشاء Task غير ضروري
616
-
funcfetchAll() async {
617
-
Task { awaitfetchUsers() }
618
-
Task { awaitfetchPosts() }
633
+
funcdoWork() async {
634
+
635
+
// هذا سيرجع عندما ترجع جميع المهام الفرعية، تلقي خطأ، أو يتم إلغاؤها
636
+
let result =tryawaitwithThrowingTaskGroup() { group in
637
+
group.addTask {
638
+
tryawaitself.performAsyncOperation1()
639
+
}
640
+
group.addTask {
641
+
tryawaitself.performAsyncOperation2()
642
+
}
643
+
// انتظر واجمع نتائج المهام هنا
644
+
}
619
645
}
620
646
621
-
// أفضل - استخدم التزامن المنظم
622
-
funcfetchAll() async {
623
-
asynclet users =fetchUsers()
624
-
asynclet posts =fetchPosts()
625
-
await (users, posts)
647
+
funcperformAsyncOperation1() asyncthrows->Int {
648
+
return1
649
+
}
650
+
funcperformAsyncOperation2() asyncthrows->Int {
651
+
return2
652
+
}
653
+
```
654
+
655
+
لجمع نتائج المهام الفرعية للمجموعة، يمكنك استخدام حلقة for-await-in:
656
+
657
+
```swift
658
+
var sum =0
659
+
forawait result in group {
660
+
sum += result
626
661
}
662
+
// sum == 3
627
663
```
628
664
629
-
إذا كنت بالفعل في سياق async، فضّل التزامن المنظم (`async let`، `TaskGroup`) على إنشاء `Task`s غير منظمة. التزامن المنظم يتعامل مع الإلغاء تلقائياً ويجعل الكود أسهل للفهم.
665
+
يمكنك معرفة المزيد عن [TaskGroup](https://developer.apple.com/documentation/swift/taskgroup) في توثيق Swift.
666
+
667
+
#### ملاحظة حول المهام وSwiftUI.
668
+
669
+
عند كتابة واجهة مستخدم، غالباً تريد بدء مهام غير متزامنة من سياق متزامن. مثلاً، تريد تحميل صورة بشكل غير متزامن كاستجابة للمس عنصر واجهة. بدء مهام غير متزامنة من سياق متزامن غير ممكن في Swift. لهذا ترى حلولاً تتضمن `Task { ... }`، مما يُدخل مهاماً غير مُدارة.
670
+
671
+
لا يمكنك استخدام `TaskGroup` من مُعدّل SwiftUI متزامن لأن `withTaskGroup()` دالة async أيضاً وكذلك دوالها المرتبطة.
672
+
673
+
كبديل، SwiftUI يوفر مُعدّلاً غير متزامن يمكنك استخدامه لبدء عمليات غير متزامنة. المُعدّل `.task { }`، الذي ذكرناه سابقاً، يقبل دالة `() async -> Void`، مثالية لاستدعاء دوال `async` أخرى. متاح على كل `View`. يُفعّل قبل ظهور الواجهة والمهام التي ينشئها مُدارة ومرتبطة بدورة حياة الواجهة، مما يعني أن المهام تُلغى عندما تختفي الواجهة.
674
+
675
+
عودة لمثال اللمس-لتحميل-صورة: بدلاً من إنشاء مهمة غير مُدارة لاستدعاء دالة `loadImage()` غير المتزامنة من دالة `.onTap() { ... }` المتزامنة، يمكنك تبديل علَم عند لفتة اللمس واستخدام المُعدّل `task(id:)` لتحميل الصور بشكل غير متزامن عندما تتغير قيمة `id` (العلَم).
676
+
677
+
هنا مثال:
678
+
679
+
```swift
680
+
structContentView: View {
681
+
682
+
@Stateprivatevar shouldLoadImage =false
683
+
684
+
var body: some View {
685
+
Button("اضغط هنا!") {
686
+
// بدّل العلَم
687
+
shouldLoadImage =!shouldLoadImage
688
+
}
689
+
// الـ View تدير المهمة الفرعية
690
+
// تبدأ قبل عرض الواجهة
691
+
// وتتوقف عندما تختفي الواجهة
692
+
.task(id: shouldLoadImage) {
693
+
// عندما تتغير قيمة العلَم، SwiftUI يعيد تشغيل المهمة
<h4>Task and Task.detached are an anti-pattern</h4>
470
470
471
-
The Swift team recommends [Task.detached as a last resort](https://forums.swift.org/t/revisiting-when-to-use-task-detached/57929). It doesn't inherit priority, task-local values, or actor context. Most of the time, regular `Task` is what you want. If you need CPU-intensive work off the main actor, mark the function `@concurrent` instead.
471
+
The tasks you schedule with `Task { ... }` are not managed. There is no way for you to cancel them or to know when they finish, if ever. There is no way to access their return value or to know if they encounter an error. In the majority of the cases, it will be better to use tasks managed by a `.task` or `TaskGroup`, [as explained in the "Common mistakes" section](#managedtasks).
472
+
473
+
[Task.detached should be your last resort](https://forums.swift.org/t/revisiting-when-to-use-task-detached/57929). Detached tasks don't inherit priority, task-local values, or actor context. If you need CPU-intensive work off the main actor, mark the function `@concurrent` instead.
472
474
</div>
473
475
474
476
<divclass="analogy">
@@ -607,24 +609,93 @@ func badIdea() async {
607
609
608
610
Swift's cooperative thread pool has limited threads. Blocking one with `DispatchSemaphore`, `DispatchGroup.wait()`, or similar calls can cause deadlocks. If you need to bridge sync and async code, use `async let` or restructure to stay fully async.
609
611
610
-
### Creating unnecessary Tasks
612
+
<divid="managedtasks">
613
+
614
+
### Create unmanaged tasks
615
+
616
+
Tasks that you create manually with `Task { ... }` or `Task.detached { ... }` are not managed. After you create unmanaged tasks, you can't control them. You can't cancel them if the task from which you started it is cancelled. You can't know if they finished their work, if they threw an error, or collect their return value. Starting such a task is like throwing a bottle into the sea and hoping it will deliver its message to its destination, without ever seeing that bottle again.
617
+
618
+
<divclass="analogy">
619
+
<h4>The Office Building</h4>
620
+
621
+
A `Task` is like assigning work to an employee. The employee handles the request (including waiting for other offices) while you continue with your immediate work.
622
+
623
+
After you dispatch work to the employee, you have no means to communicate with her. You can't tell her to stop the work or know if she finished and what the result of that work was.
624
+
625
+
What you actually want is to give the employee a walkie-talkie to communicate with her while she handles the request. With the walkie-talkie, you can tell her to stop, or she can tell you when she encounters an error, or she can report the result of the request you gave her.
626
+
</div>
627
+
628
+
Instead of creating unmanaged tasks, use Swift concurrency to keep control of the subtasks you create. Use `TaskGroup` to manage a (group of) subtask(s). Swift provides a couple of `withTaskGroup() { group in ... }` functions to help create task groups.
611
629
612
630
```swift
613
-
// Unnecessary Task creation
614
-
funcfetchAll() async {
615
-
Task { awaitfetchUsers() }
616
-
Task { awaitfetchPosts() }
631
+
funcdoWork() async {
632
+
633
+
// this will return when all subtasks return, throw an error, or are cancelled
634
+
let result =tryawaitwithThrowingTaskGroup() { group in
635
+
group.addTask {
636
+
tryawaitself.performAsyncOperation1()
637
+
}
638
+
group.addTask {
639
+
tryawaitself.performAsyncOperation2()
640
+
}
641
+
// wait for and collect the results of the tasks here
642
+
}
617
643
}
618
644
619
-
// Better - use structured concurrency
620
-
funcfetchAll() async {
621
-
asynclet users =fetchUsers()
622
-
asynclet posts =fetchPosts()
623
-
await (users, posts)
645
+
funcperformAsyncOperation1() asyncthrows->Int {
646
+
return1
647
+
}
648
+
funcperformAsyncOperation2() asyncthrows->Int {
649
+
return2
624
650
}
625
651
```
626
652
627
-
If you're already in an async context, prefer structured concurrency (`async let`, `TaskGroup`) over creating unstructured `Task`s. Structured concurrency handles cancellation automatically and makes the code easier to reason about.
653
+
To collect the results of the group's child tasks, you can use a for-await-in loop:
654
+
655
+
```swift
656
+
var sum =0
657
+
forawait result in group {
658
+
sum += result
659
+
}
660
+
// sum == 3
661
+
```
662
+
663
+
You can learn more about [TaskGroup](https://developer.apple.com/documentation/swift/taskgroup) in the Swift documentation.
664
+
665
+
#### Note about Tasks and SwiftUI.
666
+
667
+
When writing a UI, you often want to start asynchronous tasks from a synchronous context. For example, you want to asynchronously load an image as a response to a UI element touch. Starting asynchronous tasks from a synchronous context is not possible in Swift. This is why you see solutions involving `Task { ... }`, which introduces unmanaged tasks.
668
+
669
+
You can't use `TaskGroup` from a synchronous SwiftUI modifier because `withTaskGroup()` is an async function too and so are its related functions.
670
+
671
+
As an alternative, SwiftUI offers an asynchronous modifier that you can use to start asynchronous operations. The `.task { }` modifier, which we already mentioned, accepts a `() async -> Void` function, ideal for calling other `async` functions. It is available on every `View`. It is triggered before the view appears and the tasks it creates are managed and bound to the lifecycle of the view, meaning the tasks are cancelled when the view disappears.
672
+
673
+
Back to the tap-to-load-an-image example: instead of creating an unmanaged task to call an asynchronous `loadImage()` function from a synchronous `.onTap() { ... }` function, you can toggle a flag on the tap gesture and use the `task(id:)` modifier to asynchronoulsy load images when the `id` (the flag) value changes.
674
+
675
+
Here is a example:
676
+
677
+
```swift
678
+
structContentView: View {
679
+
680
+
@Stateprivatevar shouldLoadImage =false
681
+
682
+
var body: some View {
683
+
Button("Click Me !") {
684
+
// toggle the flag
685
+
shouldLoadImage =!shouldLoadImage
686
+
}
687
+
// the View manages the subtask
688
+
// it starts before the view is displayed
689
+
// and stops when the view is hidden
690
+
.task(id: shouldLoadImage) {
691
+
// when the flag value changes, SwiftUI restarts the task
692
+
guard shouldLoadImage else { return }
693
+
awaitloadImage()
694
+
}
695
+
}
696
+
}
697
+
```
698
+
</div>
628
699
629
700
</div>
630
701
</section>
@@ -702,8 +773,9 @@ Choose your agent and run the commands below:
0 commit comments