agentsclimarketplace

Async tasks

Skill ortus-boxlang/skills/boxlang-core-development/async-tasks

BoxLang AI skills repository and Claude Plugin

Install
npx -y skills add ortus-boxlang/skills --skill async-tasks

Assembled from the repository path, not quoted from the project. Check it against their README if it does not work.

2 things to look at

  • no licenseNo license file was found in the repository. Code published without one is not open source by default, so using it at work is a question for whoever answers licensing questions where you are.
  • 0 stars0 stars. Stars are a popularity signal and not a quality one, but at this level it is likely that nobody has read this closely except its author, and you would be relying on your own review.

What its author says it does

Copied from the file, not written here

Use this skill when working with BoxLang asynchronous programming: BoxFuture (CompletableFuture extension), AsyncService, executor types (VIRTUAL/FIXED/CACHED/SCHEDULED), BaseScheduler, ScheduledTask fluent API, scheduling with cron constraints, task lifecycle callbacks, and registerring schedulers via ModuleConfig.bx.

SKILL.md

10.4 KB, as published. Nobody here has run it

BoxLang Async & Scheduled Tasks

Overview

BoxLang provides first-class async support built on JRE 21 virtual threads and CompletableFuture. Key classes:

ClassPackagePurpose
BoxFuture<T>ortus.boxlang.runtime.asyncExtended CompletableFuture
AsyncServiceortus.boxlang.runtime.servicesExecutor and future factory
BaseSchedulerortus.boxlang.runtime.async.tasksExtend to create a scheduler
ScheduledTaskortus.boxlang.runtime.async.tasksFluent scheduled task builder
BoxExecutorortus.boxlang.runtime.async.executorsThead pool wrapper

AsyncService — Executor Factory

import ortus.boxlang.runtime.services.AsyncService;
import ortus.boxlang.runtime.async.executors.BoxExecutor;
import ortus.boxlang.runtime.async.BoxFuture;

AsyncService asyncService = BoxRuntime.getInstance().getAsyncService();

// Create executors
BoxExecutor virtualExec   = asyncService.newExecutor( "myPool", ExecutorType.VIRTUAL );   // Virtual threads (default)
BoxExecutor fixedExec     = asyncService.newExecutor( "myFixed", ExecutorType.FIXED, 4 ); // Fixed 4-thread pool
BoxExecutor cachedExec    = asyncService.newExecutor( "myCached", ExecutorType.CACHED );  // Cached thread pool
BoxExecutor scheduledExec = asyncService.newScheduledExecutor( "myScheduled" );           // For scheduled tasks

// Get an existing executor
BoxExecutor exec = asyncService.getExecutor( "myPool" );

// Check existence
boolean exists = asyncService.hasExecutor( "myPool" );

// Shut it down
asyncService.shutdownExecutor( "myPool" );

Executor Types

TypeBacked ByBest For
VIRTUALExecutors.newVirtualThreadPerTaskExecutor()I/O-bound work (default)
FIXEDExecutors.newFixedThreadPool(n)CPU-bound work with known concurrency
CACHEDExecutors.newCachedThreadPool()Short-lived tasks with variable load
SCHEDULEDScheduledThreadPoolExecutorScheduled and recurring tasks

BoxFuture — Async Work

BoxFuture<T> extends CompletableFuture<T> with fluent BoxLang helpers.

import ortus.boxlang.runtime.async.BoxFuture;

// Create a future that runs work asynchronously
BoxFuture<String> future = asyncService.newFuture( () -> fetchFromRemote( url ) );

// Chain transformations
future
    .thenApply( result -> result.toUpperCase() )
    .thenAccept( result -> System.out.println( "Got: " + result ) )
    .exceptionally( err -> {
        logger.error( "Failed: {}", err.getMessage() );
        return null;
    });

// Block and get result (with timeout)
String result = future.get( 10, TimeUnit.SECONDS );

// All-of: wait for multiple futures
BoxFuture<Void> all = asyncService.allOf(
    asyncService.newFuture( () -> taskA() ),
    asyncService.newFuture( () -> taskB() )
);
all.join();

// Any-of: first to complete wins
BoxFuture<Object> any = asyncService.anyOf(
    asyncService.newFuture( () -> serviceA() ),
    asyncService.newFuture( () -> serviceB() )
);
Object first = any.join();

BoxFuture in BoxLang Script

// Using runAsync BIF
var future = runAsync( () -> {
    return fetchData()
})

// Chain
var result = future
    .then( (v) -> v.toUpperCase() )
    .get()

// Parallel execution
var futures = [
    runAsync( () -> fetchUsers() ),
    runAsync( () -> fetchOrders() )
]

// Wait for all
var all = BoxFuture::allOf( futures )
all.join()

BaseScheduler — Creating a Scheduler

Extend BaseScheduler to define a scheduler with one or more tasks. Override configure() to register tasks.

Java Scheduler

import ortus.boxlang.runtime.async.tasks.BaseScheduler;
import ortus.boxlang.runtime.async.tasks.ScheduledTask;

public class MyModuleScheduler extends BaseScheduler {

    public MyModuleScheduler() {
        super( "my-module-scheduler" );
    }

    @Override
    public void configure() {
        // Register tasks using the fluent task() API
        task( "cleanupExpiredSessions" )
            .call( () -> sessionService.cleanExpired() )
            .every( 15, TimeUnit.MINUTES );

        task( "generateDailyReport" )
            .call( () -> reportService.generate() )
            .dailyAt( "02:00" );

        task( "healthCheck" )
            .call( () -> monitor.ping() )
            .every( 30, TimeUnit.SECONDS )
            .withNoOverlaps();
    }

    // Optional lifecycle callbacks
    @Override
    public void onStartup() {
        this.logger.info( "MyModuleScheduler started" );
    }

    @Override
    public void onShutdown() {
        this.logger.info( "MyModuleScheduler shutting down" );
    }

    @Override
    public void onError( ScheduledTask task, Exception e ) {
        this.logger.error( "Task [{}] failed: {}", task.getName(), e.getMessage(), e );
    }

    @Override
    public void onSuccess( ScheduledTask task, Object result ) {
        this.logger.debug( "Task [{}] succeeded", task.getName() );
    }
}

BoxLang Scheduler

// schedulers/MyScheduler.bx
class extends="ortus.boxlang.runtime.async.tasks.BaseScheduler" {

    function configure() {
        // Tasks registered via the Java task() API called through DynamicObject
        task( "cleanup" )
            .call( () -> cleanupExpiredData() )
            .everyHour()

        task( "report" )
            .call( () -> generateReport() )
            .dailyAt( "06:00" )
    }

}

ScheduledTask — Fluent API

ScheduledTask has a rich fluent API for defining frequency and constraints.

Frequency Methods

ScheduledTask t = task( "myTask" ).call( callable );

// Time-based intervals
t.every( 5, TimeUnit.MINUTES );
t.everyMinute();
t.everyHour();
t.everyDay();
t.everyWeek();
t.everyMonth();

// Delay before first run
t.startOn( LocalDateTime.now().plusHours( 1 ) );
t.withDelay( 30, TimeUnit.SECONDS );

// Daily at a specific time
t.dailyAt( "08:00" );      // HH:mm
t.weeklyOn( 2, "08:00" );  // Day of week (1=Mon), time

// Monthly
t.monthlyOn( 1, "00:00" ); // 1st of month at midnight

Constraint Methods

// Day constraints
t.onWeekends();         // Only Sat/Sun
t.onWeekdays();         // Only Mon-Fri
t.onMonday();
t.onTuesday();
// ... onWednesday(), onThursday(), onFriday(), onSaturday(), onSunday()

t.onFirstBusinessDayOfMonth();
t.onLastBusinessDayOfMonth();
t.dayOfMonth( 15 );     // Specific day

// Dynamic constraint — skip task if predicate returns false
t.when( (task) -> systemIsReady() );

// Prevent overlapping runs
t.withNoOverlaps();   // Don't fire if previous run still active

// Disable this task (useful for debugging)
t.disable();
// or use xtask() from the scheduler:
// xtask("myTask").call(callable).everyHour();

Callbacks

t.before( (task) -> logger.info("Starting {}", task.getName()) )
 .after( (task, result) -> logger.info("Done {}", task.getName()) )
 .onFailure( (task, err) -> alertTeam(err) )
 .onSuccess( (task, result) -> metrics.increment("task.success") );

Registering a Scheduler in ModuleConfig.bx

// ModuleConfig.bx
class {

    function onLoad() {
        // Register and start a Java scheduler
        var scheduler = createObject( "java", "com.example.schedulers.MyModuleScheduler" ).init()
        boxRuntime.getSchedulerService().registerScheduler( scheduler )
        scheduler.startup()

        log.info( "MyModuleScheduler registered and started" )
    }

    function onUnload() {
        // Schedulers are auto-shutdown by SchedulerService on runtime shutdown.
        // Explicit shutdown only needed for early cleanup:
        boxRuntime.getSchedulerService().removeScheduler( "my-module-scheduler" )
        log.info( "MyModuleScheduler removed" )
    }

}

SchedulerService API

import ortus.boxlang.runtime.services.SchedulerService;

SchedulerService schedulerSvc = BoxRuntime.getInstance().getSchedulerService();

// Check if a scheduler is registered
boolean has = schedulerSvc.hasScheduler( "my-module-scheduler" );

// Get an existing scheduler
BaseScheduler sched = schedulerSvc.getScheduler( "my-module-scheduler" );

// Remove (also calls shutdown)
schedulerSvc.removeScheduler( "my-module-scheduler" );

// List all registered schedulers
Set<String> names = schedulerSvc.getSchedulerNames();

Task Lifecycle Events (Interceptors)

These BoxEvent keys fire for every task in every scheduler:

EventWhen
schedulerBeforeAnyTaskBefore a task runs
schedulerAfterAnyTaskAfter a task runs
schedulerOnAnyTaskSuccessTask completed without error
schedulerOnAnyTaskErrorTask threw an exception
onSchedulerStartupScheduler started
onSchedulerShutdownScheduler stopped

Testing Schedulers

// Create and run in isolation
MyModuleScheduler scheduler = new MyModuleScheduler();
scheduler.configure();

// Get a registered task
ScheduledTask task = scheduler.getTask( "cleanupExpiredSessions" );
assertNotNull( task );

// Run a task manually (bypasses scheduling)
task.run();
// tests/specs/SchedulerTest.bx
class extends="testbox.system.BaseSpec" {

    function run() {
        describe( "MyScheduler tasks", () -> {
            it( "should have the cleanup task registered", () -> {
                var scheduler = new com.example.schedulers.MyModuleScheduler()
                scheduler.configure()
                expect( scheduler.hasTask("cleanupExpiredSessions") ).toBeTrue()
            })
        })
    }

}

References

Keep looking

Skills are one crate of 328,083. Ordering is by how many stacks a row turns up in, so the top of any crate is what has actually been picked rather than what has the most stars.