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

The 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:

For Java Developers: Java developers can also use BoxFuture directly in their Java code to interact with BoxLang's asynchronous ecosystem seamlessly.

⚡ Core Methods

🥇 Completion Methods

get() - Blocking Retrieval

Please 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 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

🔄 Transformation Methods

then() vs thenAsync() - Understanding the Difference

The 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

Method
Thread Usage
Best For
Performance

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

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

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

Learn More: For detailed information about parallel processing patterns, see Parallel Computations.

📚 Common Usage Patterns

🔄 Chaining Operations

🎯 Async Pipelines

Advanced Patterns: For complex pipeline patterns and composition strategies, see Async Pipelines.

🛡️ Error Recovery

⏱️ Timeout Handling

🔧 Working with Executors

BoxFutures integrate seamlessly with BoxLang's executor system:

Learn More: For detailed information about executor types and configuration, see Executors.

🎯 Best Practices

✅ Do's

  • Use appropriate timeouts for operations that might hang

  • Handle errors gracefully with onError() or exceptionally()

  • Chain operations instead of nesting callbacks

  • Choose the right executor for your workload type

  • Use getAsAttempt() for functional error handling

  • Combine 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 transformations

  • Use thenAsync() for heavy computations to avoid blocking the main thread

  • Use 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?