@@ -109,13 +109,14 @@ Goodbye, World
109109``` php
110110function mergeFiles(string ...$files): string
111111{
112- $taskGroup = new Async\TaskGroup(captureResults: true);
112+ $scope = new Async\Scope();
113+ $taskGroup = new Async\TaskGroup(scope: $scope, captureResults: true);
113114
114115 foreach ($files as $file) {
115116 $taskGroup->spawn(file_get_contents(...), $file);
116117 }
117118
118- return array_merge("\n", await $taskGroup->all() );
119+ return array_merge("\n", await $taskGroup);
119120}
120121```
121122
@@ -160,7 +161,7 @@ function fetchUserProfile(string $userId): array
160161{
161162 async inherit $userDataScope {
162163
163- $taskGroup = new Async\TaskGroup($userDataScope);
164+ $taskGroup = new Async\TaskGroup($userDataScope, captureResults: true );
164165
165166 $taskGroup->add(spawn fetchUserData());
166167 $taskGroup->add(spawn fetchUserSettings($userId));
@@ -1658,7 +1659,7 @@ function generateReport(): void
16581659 try {
16591660 async inherit $scope {
16601661
1661- $taskGroup = new TaskGroup();
1662+ $taskGroup = new TaskGroup($scope );
16621663
16631664 [$employees, $salaries, $workHours] = await $taskGroup->add([
16641665 spawn fetchEmployees(),
@@ -1982,7 +1983,8 @@ can set a custom timeout for that specific `Scope` using the `Scope::disposeAfte
19821983``` php
19831984function mergeFiles(string ...$files): string
19841985{
1985- $taskGroup = new Async\TaskGroup(captureResults: true);
1986+ $scope = new Async\Scope();
1987+ $taskGroup = new Async\TaskGroup($scope, captureResults: true);
19861988
19871989 foreach ($files as $file) {
19881990 $taskGroup->add(spawn file_get_contents($file));
@@ -2026,7 +2028,8 @@ If the results are no longer needed, the `TaskGroup::disposeResults()` method sh
20262028function processInBatches(array $files, int $limit): array
20272029{
20282030 $allResults = [];
2029- $taskGroup = new Async\TaskGroup(captureResults: true);
2031+ $scope = new Async\Scope();
2032+ $taskGroup = new Async\TaskGroup($scope, captureResults: true);
20302033 $count = 0;
20312034
20322035 foreach ($files as $file) {
@@ -2063,7 +2066,44 @@ The reason for this behavior lies in the fact that `TaskGroup` only keeps track
20632066If a task group is being disposed, it means the user clearly understands
20642067that all coroutines launched within it should also be terminated.
20652068
2066- #### Combining TaskGroup and Scope
2069+ #### Adding coroutines to a ` TaskGroup `
2070+
2071+ The method ` TaskGroup::add ` adds a new coroutine to the ` TaskGroup ` . It performs the following checks:
2072+ * the coroutine must not be completed
2073+ * the coroutine must belong to the same ` Scope ` associated with the ` TaskGroup `
2074+ * A task must not be added more than once.
2075+
2076+ If these checks fail, an exception is thrown.
2077+
2078+ ``` php
2079+ use Async\TaskGroup;
2080+
2081+ $scope = new Async\Scope();
2082+ $taskGroup = new Async\TaskGroup($scope);
2083+
2084+ $taskGroup->add(spawn {return;}); // <- Exception: Wrong scope
2085+
2086+ $coroutine = spawn with $scope {return;};
2087+
2088+ suspend;
2089+ $taskGroup- >add($coroutine); // <- Exception: Task already completed
2090+
2091+ $coroutine = spawn with $scope {return;};
2092+ $taskGroup- >add($coroutine);
2093+ $taskGroup->add($coroutine); // <- Exception: Task already added
2094+ ```
2095+
2096+ #### TaskGroup and Scope
2097+
2098+ The `TaskGroup` constructor always requires an explicit `Scope` to be defined.
2099+ This is done to make the developer consider how long the tasks in the `TaskGroup` should live.
2100+
2101+ `TaskGroup` ties its lifetime to that of the `Scope` and cannot outlive it.
2102+ As a result, a `TaskGroup` cannot " hang in the air" like zombie coroutines do.
2103+
2104+ `TaskGroup` does not increase the reference count of the `$scope` object and does not prevent it from being disposed.
2105+ When the `Scope` associated with the `TaskGroup` enters the disposal phase,
2106+ it first releases all tasks within the `TaskGroup`.
20672107
20682108Combining `Scope` and `TaskGroup` helps implement the pattern of primary and secondary tasks,
20692109where tasks in the `TaskGroup` are treated as explicit, primary tasks that must be monitored for completion.
@@ -2078,20 +2118,24 @@ use Async\TaskGroup;
20782118function targetTask(int $i): void
20792119{
20802120 spawn {
2081- // subtask
2121+ // subtask should be added to the same scope
20822122 };
20832123}
20842124
2085- $scope = new Scope();
2086- $taskGroup = new TaskGroup(scope: $scope, captureResults: true);
2125+ $scope = new Scope(); // <- $scope reference equals one.
2126+ // TaskGroup will be binded to the $scope
2127+ $taskGroup = new TaskGroup(scope: $scope, captureResults: true); // <- Scope reference equals one.
20872128
20882129for($i = 0; $i < 10; $i++) {
20892130 $taskGroup- >add(spawn with $scope targetTask($i));
20902131}
20912132
20922133try {
2134+ // wait for only the tasks that were added to the TaskGroup
20932135 $results = await $taskGroup;
20942136} finally {
2137+ // First dispose the task group
2138+ // then dispose the scope
20952139 $scope->dispose();
20962140}
20972141```
@@ -2176,7 +2220,8 @@ with the only difference being that it allows you to pass a specific exception.
21762220``` php
21772221use Async\TaskGroup;
21782222
2179- $taskGroup = new Async\TaskGroup(captureResults: false);
2223+ $scope = new Async\Scope();
2224+ $taskGroup = new Async\TaskGroup(scope: $scope, captureResults: false);
21802225$taskGroup->add(spawn {
21812226 try {
21822227 suspend;
@@ -2223,7 +2268,8 @@ Using the option `$nullOnFail`, you can specify that the results of failed
22232268tasks should be filled with ` NULL ` instead.
22242269
22252270``` php
2226- $taskGroup = new Async\TaskGroup(captureResults: true);
2271+ $scope = new Async\Scope();
2272+ $taskGroup = new Async\TaskGroup($scope, captureResults: true);
22272273$taskGroup->add(spawn {return 'result 1';});
22282274$taskGroup->add(spawn {throw new Exception('Error')});
22292275
0 commit comments