Re: PHP True Async RFC Stage 4

From: Date: Thu, 23 Oct 2025 16:40:37 +0000
Subject: Re: PHP True Async RFC Stage 4
References: 1 2 3 4 5 6 7 8 9 10 11 12 13 14  Groups: php.internals 
Request: Send a blank email to internals+get-128935@lists.php.net to get a copy of this message
> On Oct 23, 2025, at 04:18, Rob Landers <rob@bottled.codes> wrote: > > On Thu, Oct 23, 2025, at 09:59, Edmond Dantes wrote: >> >> >> I’d like you to have the full information necessary to make an >> informed decision. >> >> There are two or more channel objects that need to be awaited. How >> should this be implemented? >> >> Currently >> ```php >> [$result1, $result2] = awaitAny($channel1, $channel2); >> ``` >> > As for which one I'd rather have, I'd rather have the one where my examples hold > true: > > assert(awaitAll($a, $b, $c) === [await($a), await($b), await($c)]); > > It's clear what it does and that it is just a shorthand. When that fails, we have issues. > > — Rob Just to add to this, in Swift, a Task (the analogue to this proposal's Coroutine) can emit, exactly once, a value or an error (exception). Attempting to read its value (or error) multiple times gets you the same value back. This is very handy in memoization patterns. In this example, multiple readers can call getImage() with the same id and get a value, regardless of whether they're the first (which kicks off image loading), whether the image loading has completed, or whether the image load is still in progress. (Technically, one could omit the LoadingBox wrapper entirely, but that would cause the Task to persist after it's completed, and that's a more expensive structure than a simple enum.) ```swift private enum LoadingBox<T : Sendable> { case inProgress(Task<T, Never>) //Task<Success, Failure> case ready(T) var value: T { get async { switch self { case .inProgress(let task): return await task.value case .ready(let value): return value } } } } private var imageCache: [ImageId : LoadingBox<Image>] = [:] func getImage(_ id: ImageId) async -> Image { if let cache = imageCache[id] { return await cache.value } let task = Task { ...load the image... } imageCache[id] = task return await task.value } ``` Ultimately, I would want to be able to write code such as this in PHP (with the generics, of course, but I'm not holding my breath). If coroutines sometimes return single values and sometimes return multiple values, it becomes much harder to write a generic async cache/memoization system, because you can't indicate via the type system that a single value is expected. -John

« previous php.internals (#128935) next »