Getting components to redraw while the event loop is blocked

I stumbled on exact same App pattern and felt a little short with the terse replies given my still novice expertise in JUCE. So here are the practical details on one method for the folks like me… (once you understand, you will discover additional ones, JUCE is magic!)

Context: I have my App logic implemented in a separate AppController class (not inheriting from any GUI Component) that implements just the ActionListener. The AppController gets actions (asynchronously) from GUI components elsewhere. At some point, my AppController gets an action that does a lot of things, taking many seconds and I wanted to update a TextEditor component with a kind of progress log. So we have a long action like:
… execute some logic … update GUI component … logic … update GUI … logic … update GUI… etc.
I discovered like Xenakios that the logic and text updates are executed, but the thread is busy in my AppController and JUCE main message loop never gets a chance to handle the queued Text GUI updates. All updates are cummulated and would only display in block when returning from the actionListenerCallback(). Fact is, the actionListenerCallback() actually runs within the main message thread, thus blocking it from propagating the requested GUI component updates to the display. And forget about calling repaint() directly, that only gets queued too.
My approach was then to invoke, after each GUI component update:
MessageManager::getInstance()->runDispatchLoopUntil(200);
… which worked well; yet this is not recommended and the code gets blotted with plenty such artificial calls that definitely smell like old sequential programming in an App environment that is designed for being fully event driven…

So I did, as recommended by Jules: “use a thread”… this translates to:

void ProcessController::actionListenerCallback(const String& message)
{ 
    // test which action message...
    if ( thisIsMyLongAction(message) )
         juce::Thread::launch([this] { doLongAction(); });
    else // another action ...
}

and bang! it fails because we cannot invoke any GUI component updates in a “foreign” thread, i.e. outside the main message thread. This is indeed quite logic to maintain thread safety in GUI operations. So what?
The solution is then to still launch a separate thread but wrap any function that would invoke directly or indirectly a GUI component update inside a method like

void ProcessView::appendLog(String logLine)
{
    const MessageManagerLock mmlck; // the MAGIC! just declare it, nothing more...
    logText->setText(logText->getText() + logLine + "\n"); // update a GUI component
}

and now we have: … long logic… appendLog(“done this”); … long logic … appendLog(“done that”); …

and the App is live