BoxFutures
Asynchronous Programming Made Simple
🙋 What Are BoxFutures?
BoxFutures are BoxLang's enhanced version of Java's CompletableFuture, designed to provide powerful asynchronous programming capabilities with dynamic constructs tailored for the BoxLang ecosystem. They represent a computation that will complete at some point in the future, allowing you to write non-blocking, concurrent code that's both elegant and efficient.
Task Start → BoxFuture → Result Ready
📋 ⚡ ✅
(now) (processing) (later)🎯 Why Use BoxFutures?
Non-blocking Operations: Execute tasks asynchronously without blocking the main thread
Fluent API: Chain operations together with intuitive method names
Error Handling: Built-in exception handling and recovery mechanisms
Composability: Combine multiple futures for complex workflows
Dynamic Integration: Seamlessly work with BoxLang's dynamic nature
Enhanced Timeouts: Advanced timeout handling with custom behavior
Result Transformation: Transform and map results through fluent pipelines
🚀 Creating BoxFutures
Using the futureNew() BIF
futureNew() BIFThe primary way to create BoxFutures is through the futureNew() Built-In Function (BIF):
Using Direct Java Import
For advanced use cases or when working directly with Java interop, you can import the BoxFuture class and use its static methods:
⚡ Core Methods
🥇 Completion Methods
get() - Blocking Retrieval
get() - Blocking RetrievalPlease note that even if you pass a timeout, the thread will continue to execute. This is useful for scenarios where you want to ensure the task is running without blocking indefinitely. You can try to cancel the future if needed, but it is not guaranteed to stop the execution immediately. The only real way to stop the execution is to attach it to an executor that can handle cancellation properly.
join() - Non-blocking Alternative
join() - Non-blocking AlternativeJoin is a non-blocking alternative to get() that waits for the future to complete without throwing exceptions, if you need to handle exceptions, use get() instead.
getOrDefault() - Safe Retrieval
getOrDefault() - Safe Retrieval🔄 Transformation Methods
then() vs thenAsync() - Understanding the Difference
then() vs thenAsync() - Understanding the DifferenceThe key difference between then() and thenAsync() lies in where and how the transformation function executes:
🔄 then() - Synchronous Execution Context
Executes in the same thread that completes the future
Uses the calling thread or the completing thread
Blocking - the transformation runs immediately when the future completes
Best for lightweight, fast transformations
⚡ thenAsync() - Asynchronous Execution Context
Executes in a different thread from the default executor pool
Non-blocking - the transformation is submitted to an executor
Best for heavy computations or I/O operations
Provides better thread isolation and resource management
Basic Usage Examples
With Custom Executors
When you specify a custom executor, both methods behave similarly but give you explicit control over thread management:
📊 Performance Comparison
then()
Same thread
Light operations
Faster handoff
thenAsync()
New thread
Heavy operations
Better isolation
then(fn, executor)
Custom thread
Controlled execution
Predictable resources
thenAsync(fn, executor)
Custom thread
Controlled async
Maximum flexibility
🎨 Practical Examples
🛡️ Error Handling
You can handle exceptions that occur during the future's execution using onError() or exceptionally() methods. They are the same method, but onError() is more idiomatic in BoxLang.
onError() - Exception Recovery
onError() - Exception Recovery⚡ IMPORTANT: When Error Handlers Are Applied
Error handlers like onError() and exceptionally() only handle exceptions from previous stages in the chain. This is crucial to understand for proper error handling:
🎯 Error Handling Strategies
Strategy 1: Early Recovery
Strategy 2: Centralized Error Handling
Strategy 3: Mixed Error Handling
⏱️ Timeout Management
Always remember that timeouts are not guaranteed to stop the execution of the future, they will only stop the waiting for the result. If you need to stop the execution, you need to attach it to an executor that can handle cancellation properly, unless you can listen to interrupts in your code and handle them accordingly.
orTimeout() - Timeout with Exception
orTimeout() - Timeout with ExceptioncompleteOnTimeout() - Timeout with Default
completeOnTimeout() - Timeout with Default🎁 Enhanced Features
🔍 Attempt Results
BoxFutures provide a unique getAsAttempt() method that returns results wrapped in an Attempt object, perfect for functional error handling. To learn more about BoxLang attempts, see BoxLang Attempts.
🎭 Fluent Aliases
BoxFutures provide intuitive aliases for common operations:
🔗 Static Utility Methods
Remember you can use these via the bif or the import.
🏃 Running Functions
📦 Value Creation
⏳ Delayed Execution
🌊 Working with Multiple Futures
📋 Combining Futures
🗺️ Parallel Processing
📚 Common Usage Patterns
🔄 Chaining Operations
🎯 Async Pipelines
🛡️ Error Recovery
⏱️ Timeout Handling
🔧 Working with Executors
BoxFutures integrate seamlessly with BoxLang's executor system:
🎯 Best Practices
✅ Do's
Use appropriate timeouts for operations that might hang
Handle errors gracefully with
onError()orexceptionally()Chain operations instead of nesting callbacks
Choose the right executor for your workload type
Use
getAsAttempt()for functional error handlingCombine with other async patterns for complex workflows
Monitor executor health and adjust pool sizes as needed
Use fluent API for readability and maintainability
Shut down executors properly to release resources
❌ Don'ts
Don't block unnecessarily - use non-blocking operations when possible
Don't ignore errors - always provide error handling
Don't create too many futures - consider batching operations
Don't forget timeouts for external operations
Don't mix blocking and non-blocking patterns inconsistently
🚦 Performance Considerations
🎯 Optimization Tips
Batch operations using
allApply()for multiple item transformationsUse
thenAsync()for heavy computations to avoid blocking the main threadUse appropriate executors based on workload characteristics
Set reasonable timeouts to prevent resource waste
Handle errors early to prevent cascade failures
Monitor executor health and adjust pool sizes as needed
📊 Monitoring
🔗 Integration Examples
HTTP Requests
Database Operations
File Processing
BoxFutures provide a powerful foundation for building responsive, scalable applications in BoxLang. By leveraging their fluent API and advanced features, you can create sophisticated asynchronous workflows that handle complex scenarios with elegance and efficiency. Please continue to learn about async programming with BoxLang by exploring the following sections:
Last updated
Was this helpful?
