From bb950396bef0bb368427db3c5ba14161b2de6b83 Mon Sep 17 00:00:00 2001 From: Joseph Cloutier Date: Mon, 1 Sep 2025 00:20:40 -0400 Subject: [PATCH] Implement `Future` aggregation functions. These are based on JavaScript's promise concurrency functions, using Lime's nomenclature. https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise#promise_concurrency --- src/lime/app/Future.hx | 129 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 129 insertions(+) diff --git a/src/lime/app/Future.hx b/src/lime/app/Future.hx index 2c89c0ce1d..89155cb866 100644 --- a/src/lime/app/Future.hx +++ b/src/lime/app/Future.hx @@ -306,6 +306,135 @@ import lime.utils.Log; future.value = value; return future; } + + // Aggregation functions + + /** + * Creates a `Future` which completes once all the input `Future`s complete + * successfully. Its completion value will be an array of the input + * `Future`s' completion values, in the order they happened. + * + * If any of the input `Future`s encounter an error, the output `Future` + * fails with the first encountered error message. + */ + public static function all(futures:Array>):Future> + { + var promise:Promise> = new Promise>(); + var results:Array = []; + + if (futures.length == 0) + { + promise.complete(results); + return promise.future; + } + + for (future in futures) + { + future.onComplete(function(result) + { + results.push(result); + if (results.length == futures.length) + { + promise.complete(results); + } + }); + future.onError(promise.error); + } + + return promise.future; + } + + /** + * Creates a promise which completes when all the input `Future`s resolve + * (whether success or failure). The completion value will be the array of + * resolved `Future`s, whose states can be examined. + */ + public static function allResolved(futures:Array>):Future>> + { + var promise:Promise>> = new Promise>>(); + + if (futures.length == 0) + { + promise.complete(futures); + return promise.future; + } + + var resolved:Int = 0; + + for (future in futures) + { + future.onComplete(function(value) + { + resolved += 1; + if (resolved == futures.length) + { + promise.complete(futures); + } + }); + future.onError(function(error) + { + resolved += 1; + if (resolved == futures.length) + { + promise.complete(futures); + } + }); + } + + return promise.future; + } + + /** + * Creates a `Future` which completes when any of the input `Future`s + * complete, with that `Future`'s completion value. If all input `Future`s + * encounter an error, the output `Future` will be rejected with an array + * containing the error messages. + */ + public static function any(futures:Array>):Future + { + var promise:Promise = new Promise(); + var errors:Array = []; + + if (futures.length == 0) + { + promise.error(errors); + return promise.future; + } + + for (future in futures) + { + future.onComplete(promise.complete); + future.onError(function(error) + { + errors.push(error); + if (errors.length == futures.length) + { + promise.error(errors); + } + }); + } + + return promise.future; + } + + /** + * Creates a `Future` that resolves once any of the input `Future`s resolve, + * with the same result as the first one to do so. + * + * If `futures` is empty, the returned `Future` will wait forever. + */ + public static function race(futures:Array>):Future + { + var promise:Promise = new Promise(); + + for (future in futures) + { + future.onComplete(promise.complete); + future.onError(promise.error); + } + + return promise.future; + } } #if (lime_threads && !html5)