1) Выполнять работу в параллельном потоке, в нём же делать задержки. Тогда главный поток будет работать.
2) Использовать таймер с помощью функций SetTimer()/KillTimer(). Требует иного подхода, чем показано выше, в том смысле что это не "задержка", а вызов функции с некоторым интервалом. Соответственно, нужен подсчет вызовов с реакцией в нужные моменты.
3) Можно мой пример модифицировать, как в самом первом, чтобы немного "разморозить" главный поток. Но выглядит такой подход несколько странно:
procedure THiAsmClass.doWork;
var
I: Integer;
begin
_hi_OnEvent(onEvent,'1');
// 20 задержек по 50 мс = 1000 мс
// После каждой задержки даём главному потоку обработать накопившиеся сообщения
for I := 1 to 20 do
begin
Sleep(50);
Applet.ProcessMessages;
end;
_hi_OnEvent(onEvent2,'2');
end;