IR
irwinrodriguez.dev
Back to docs

Advanced Patterns

Common usage patterns for progress reporting, parallel tasks, events, error handling, cancellation and task chaining.

1. Progress reporting

Update loTask.Progress (0-100) from inside the task procedure. Read the value from the main thread using a Timer.

* Inside task procedure (MyTasks.prg):
PROCEDURE LongImport(loParams, loTask)
    LOCAL i, lnTotal
    lnTotal = loParams.rowCount
    FOR i = 1 TO lnTotal
        * ... process row i ...
        loTask.Progress = INT((i / lnTotal) * 100)
    NEXT
    loTask.Result = TRANSFORM(lnTotal) + " rows imported."
ENDPROC
* In main thread -- Timer event (interval 500 ms):
PROCEDURE Timer1_Timer
    oProgressBar.Value = loTask.Progress
    IF loTask.Status == "Done" OR loTask.Status == "Failed"
        Timer1.Enabled = .F.
    ENDIF
ENDPROC

2. Parallel tasks with RunAll

RunAll executes an array of procedure names in parallel, up to the MaxWorkers limit. It returns an array of Task objects.

LOCAL loCore, laProcs[3], loResults
loCore = CREATEOBJECT("FoxCore.FoxCoreClass")
loCore.MaxWorkers = 3
laProcs[1] = "Task_ImportFile"
laProcs[2] = "Task_GenerateReport"
laProcs[3] = "Task_SendEmails"
loResults = loCore.RunAll(@laProcs)
* loResults is an array of Task objects, one per procedure
? loResults[1].Status
? loResults[2].Status
? loResults[3].Status

3. Event callbacks

Assign a procedure name (as a string) to OnComplete, OnFailed or OnProgress before calling Run(). FoxCore invokes the procedure on the main thread when the event occurs.

LOCAL loCore, loParams, loTask
loCore   = CREATEOBJECT("FoxCore.FoxCoreClass")
loParams = CREATEOBJECT("Empty")
ADDPROPERTY(loParams, "filePath", "C:dataimport.csv")

loTask = loCore.Run("Task_ImportCSV", loParams)
loTask.OnComplete = "HandleTaskDone"
loTask.OnFailed   = "HandleTaskError"
loTask.OnProgress = "HandleProgress"

* ...
PROCEDURE HandleTaskDone(loTask)
    MESSAGEBOX("Done: " + loTask.Result)
ENDPROC

PROCEDURE HandleTaskError(loTask)
    MESSAGEBOX("Failed: " + loTask.Error)
ENDPROC

PROCEDURE HandleProgress(loTask)
    oProgressBar.Value = loTask.Progress
ENDPROC

4. Error handling

If the task procedure throws an uncaught exception, the Task moves to Status="Failed" and loTask.Error contains the message. Always check status before reading Result.

loCore.WaitFor(loTask, 10000)
DO CASE
CASE loTask.Status == "Done"
    ? "Result: " + loTask.Result
CASE loTask.Status == "Failed"
    MESSAGEBOX("Error: " + loTask.Error, 48, "Task Failed")
CASE loTask.Status == "Cancelled"
    ? "Task was cancelled."
ENDCASE

5. Cancellation

Call loCore.Cancel(loTask) to request cancellation. The Task moves to Status="Cancelled". The task should check status periodically if it wants to respond quickly.

* Request cancellation from main thread:
loCore.Cancel(loTask)

* Inside the task procedure -- check periodically:
PROCEDURE Task_LongProcess(loParams, loTask)
    LOCAL i
    FOR i = 1 TO 1000
        IF loTask.Status == "Cancelled"
            EXIT
        ENDIF
        * ... do work ...
        loTask.Progress = i / 10
    NEXT
    IF loTask.Status != "Cancelled"
        loTask.Result = "Completed."
    ENDIF
ENDPROC

6. Sequential tasks

Use WaitFor() to chain tasks: the second task receives the result of the first as a parameter. This pattern is safe only outside UI events.

LOCAL loCore, loParams1, loParams2, loTask1, loTask2
loCore   = CREATEOBJECT("FoxCore.FoxCoreClass")
loParams1 = CREATEOBJECT("Empty")
ADDPROPERTY(loParams1, "source", "https://api.example.com/data")

* Step 1: Fetch remote data
loTask1 = loCore.Run("Task_FetchData", loParams1)
loCore.WaitFor(loTask1, 15000)

IF loTask1.Status == "Done"
    * Step 2: Process the fetched data
    loParams2 = CREATEOBJECT("Empty")
    ADDPROPERTY(loParams2, "data", loTask1.Result)
    loTask2 = loCore.Run("Task_ProcessData", loParams2)
    loCore.WaitFor(loTask2, 10000)
    ? loTask2.Result
ELSE
    ? "Fetch failed: " + loTask1.Error
ENDIF
Memory isolation: FoxCore tasks run in separate VFP instances. They do NOT share memory, global variables or SET commands with the main thread. Pass all needed data through the loParams object.

Next: API Reference ->