> > Thinking aloud, perhaps we could cause `new Fiber` to create an automatic async block? >
The main issue with Fibers is their switching logic: If you create Fiber A and call another Fiber B inside it, Fiber B can only return to the Fiber that created it, not just anywhere. However, the Scheduler requires entirely different behavior. This creates a conflict with the Scheduler. Moreover, it can even break the Scheduler if it operates based on Fibers. That's why all these strange solutions in the RFC are just workarounds to somehow bypass this problem. But it seems we've already found an alternative solution. > I cannot speak for Derick. Of course, I just mean that he probably won't be happy about it :) > No, just those functions/objects that necessarily involve running async control commands. Most wouldn't. > They would just silently context switch when they hit an IO operation (which as noted above is transparency supported, which is what makes this > work) and otherwise behave the same. So it's something more like Go or Python. > > $val = async(function(AsyncContext $ctx) use ($stuff, $fn) { > $result = []; > foreach ($stuff as $item) { > $result[] = $ctx->run($fn); >} > > // We block/wait here until all subtasks are complete, then the async() call returns this value. > return $result; > }); Do I understand correctly that at the point $val = async(function(AsyncContext $ctx) use ($stuff, $fn) execution stops until everything inside is completed? If so, let me introduce a second semantic option (for now, I'll remove the context and focus only on the function). ```php $url1 = 'https://domain1.com/'; $url2 = 'https://domain2.com/'; $url_handle = fn(string $url) => file_get_contents($url); $res = Async\start(function() use ($url1, $url2, $url_handle) { $res1 = Async\run($url_handle, $url1); $res2 = Async\run($url_handle, $url2); Async\run(fn() => sleep(5)); // some logic here return $merged_result; }); ``` What's Happening Here: 1. After calling $res = Async\start(), the code waits until the entire block completes. 2. Inside Async\start, the code waits for all nested coroutines to finish. 3. If a coroutine has other nested coroutines, the same rule applies. Rules Inside an Asynchronous Block: 1. I/O functions do not block coroutines within the block. 2. Creating a new Fiber is not allowed — an exception will be thrown: you cannot use Fiber. 3. Unhandled exceptions will be thrown at the point of $res = Async\start(). Coroutine Cancellation Rules: Canceling a coroutine cancels it and all its child coroutines (this cannot be bypassed unless the coroutine is created in a different context). How does this option sound to you? Essentially, this is Kotlin, but it should also resemble Python. However, unlike Kotlin, there are no special language constructs here—code blocks naturally serve that role. Of course, syntactic sugar can be added later for better readability. And if you like this, I have good news: there are no implementation issues at this level. In terms of semantic elegance, the only thing that bothers me is that return behavior is slightly altered — meaning the actual "return" won’t happen until all child functions complete. This isn’t very good, and Kotlin’s style would fit better here. But on the other hand — can we live with this? > I cannot speak to JS Symbols as I haven't used them. > I am just vhemently opposed to globals, no matter how many layers they're wrapped in. :-) Most uses could be replaced by proper DI or partial application. You won’t be able to use DI because you have only *one service (instance of class)* for the entire application, not a separate service for each coroutine. This service is shared across the application and can be called from any coroutine. As a result, the service needs memory slots to store or retrieve data. DI is a mechanism used once during service initialization, not every time a method is called. The only question is whether to use open text keys in the context, which is unsafe and can lead to collisions, or to use a unique key-object that is known only to the one who created it. (If PHP introduces object constants, this syntax would also look elegant.) There is, of course, another approach: making Context any arbitrary object defined by the user. But this solution has a known downside — lack of a standard interface. > (Or even just $ch->inPipe and $ch->outPipe, now that we have nice property support.) Just a brilliant idea. :) Have a good day! Ed.