Delay between end of onUpdate and other events

Since adding a second timer to my widget app, I’ve noticed an issue in the simulator: after each call to onUpdate in my view, there’s a 2-3 second delay before any other queued events (timers, user input, web responses) are processed.

If I disable (comment out) either of the two main timers, the delay disappears.

To investigate further, I added a third timer inside onUpdate that triggers a one-time callback after 50ms. This third timer doesn’t appear to contribute to the issue, the original delay still occurs only if both main timers are active.

public function onUpdate( dc as Dc ) as Void {
    EvccHelperBase.debug("WidgetSiteBase: onUpdate" );
    // Some drawing
    EvccHelperBase.debug( "WidgetSiteBase: onUpdate completed" );
    _updateCounter++;
    if( _updateCounter > 2 ) {
        var timer = new Toybox.Timer.Timer();
        timer.start( method( :testTimer ), 50, false );
    }
}
private var _updateCounter as Number = 0;
public function testTimer() as Void {
    EvccHelperBase.debug( "WidgetSiteBase: timer triggered" );
}

The test timer is used solely to ensure that a timer event is queued immediately after onUpdate completes. A counter is necessary because, during startup, I can only instantiate two timers -attempting to create a third results in an error. However, after the startup phase, I’m able to instantiate the third timer without any issues.

Here’s the log file that demonstrates the problem:

12.4.2025 17:21:42: TaskQueue: timer triggered
12.4.2025 17:21:42: TaskQueue: timer triggered
12.4.2025 17:21:42: WidgetSiteBase: onUpdate
12.4.2025 17:21:42: WidgetSiteBase: onUpdate completed
12.4.2025 17:21:44: TaskQueue: timer triggered
12.4.2025 17:21:44: WidgetSiteBase: timer triggered
12.4.2025 17:21:44: TaskQueue: timer triggered
12.4.2025 17:21:44: TaskQueue: timer triggered
12.4.2025 17:21:44: TaskQueue: timer triggered
12.4.2025 17:21:44: TaskQueue: timer triggered
12.4.2025 17:21:44: TaskQueue: timer triggered
12.4.2025 17:21:45: MultiStateRequestsTimer: timer triggered
12.4.2025 17:21:46: StateRequest: onReceive

You can see the gap between the end of onUpdate at 17:21:42 and the first timer event that follows at 17:21:44. Another timer appears to be slipping in between onUpdate and the test timer.

All my event handlers include debug output, so I’m confident that none of my code is being executed during that two-second delay.

The first of the two main timers is initialized as follows - it’s responsible for performing regular web requests:

public class EvccMultiStateRequestsHandler {
    private var _timer as Timer.Timer = new Timer.Timer();
    
    public function startRequestTimer() as Void {
        _timer.start( method( :makeRequest ), 5000, true );
    }

    public function makeRequest() as Void {
        //...
    }
}


The second timer handles a queue of tasks and is set up like this:
class EvccTaskQueue {
    private static var _instance as EvccTaskQueue?;
    public static function getInstance() as EvccTaskQueue {
        if( _instance == null ) { _instance = new EvccTaskQueue(); }
        return _instance as EvccTaskQueue;
    }

    private function initialize() {}

    private var _timer as Timer.Timer = new Timer.Timer();
    
    private function startTimer() as Void {
        _timer.start( method( :executeTask ), 50, false );
    }

    public function executeTask() as Void {
            // ...
            startTimer();
    }
}

Both code snippets are simplified, but they should capture the essence of what’s going on.

Does anyone have an idea what in my code might be causing the delay after onUpdate, and how it could be related to the two timers?

  • Not sure. I tried recreating your problem with your actual code in the sim, but it looks like I have to set up some site URL in app settings, so I didn't get to the part where the "site view" is displayed.

    I tried to create a toy example with two timers which fire every 50 ms and 5000 ms, respectively, with WatchUi.requestUpdate being called twice per second, but I didn't see anything like the problem you described. The timer callbacks and onUpdate are called without any noticeable "gaps" (def not 2 seconds worth).

    [Yeah, I realize it's possible that my example doesn't really capture your use case :/]

    To use this example:

    - create a new watch app project called TestTimers

    - replace the view file with this:

    https://pastebin.com/fsZ8gstP

    I can only guess that the delay might have something to do with what's actually being executed in onUpdate or one of the timer callbacks?

  • Thanks for giving it a shot!

    There is a public demo server that can be used as URL in the app settings to run my code: https://demo.evcc.io

    The link opens a web-based user interface, but the app will add /api/state to access a REST API.

    In the latest version on GitHub I have the test timer enabled again.

    I have been running it now on my notebook, with less computing power, and there the gaps are increasing, by a lot, often 5-10 seconds.

    I was also not able to reproduce it in a simpler example, there must be something in my code that is causing it.

  • What happens if you change the 50ms to say 150 or 200?  Understand that 50 is the fastest possible, and with a MIP device and onPartialUpdate for a watchface in low power mode, and watch face diagnostics, if you don't assign clip regions,  You can see it might take 50+ just to update the full screen.  I don't know of a way to check for Amoled devices.

    Why do you need such a fast timer?

  • Why do you need such a fast timer?

    The 50ms timer in the onUpdate is just to ensure that there is an event scheduled after onUpdate, to reproduce the delay.

    The other 50ms timer is a task queue. It triggers, a task is processed, and the timer is set again. The intention is that user input can be processed between the tasks. For that reason it is a one-time timer, set again after every task, opposed to a repeating timer. 

  • The good news is, the problem seems to be only in the simulator. On the real device it stays very response, and I can switch between views without delays.

    Still want to figure it out, it is pretty annoying for testing. I just implemented an additional view, adding to the load on that task queue, and it made the problem worse in the simulator. On the watch it is still fine ...

  • What happens if you change the 50ms to say 150 or 200? 

    I tried, but it did not really change anything. I think the 50ms should be OK, because they only start after a task is completed, and the goal is that the next task is executed as soon as possible, but with still giving the system opportunity to process user input. So if the user presses a key, while a task is processed, the key event will be processed first, and then the next task, because the timer is started only at the end, when the key event is already ahead of it in the CIQ event queue.

    With recent additions to my app it seems to get worth, now I regularly see gaps around 10 seconds:

    14.4.2025 22:12:57: TaskQueue: Executing task 1/22 ...
    14.4.2025 22:12:57: WidgetSiteBase: onUpdate main site=1
    14.4.2025 22:12:57: WidgetSiteBase: onUpdate completed for main site=1
    14.4.2025 22:13:9: WidgetSiteBase: timer triggered
    14.4.2025 22:13:9: TaskQueue: Executing task 1/21 ...
    14.4.2025 22:13:10: TaskQueue: Executing task 1/20 ...

    So, the sheer number of times the one-time timer gets started may contribute. But still, I can get rid of the issue if I disable either of my two main timers. So if I remove the 5s repeating timer but keep the 50ms as it is, the problem does not occur.

  • I replaced now the Method.invoke() calls in my timer event handlers with direct calls to methods, since Method.invoke() does not have proper exception handling.

    But I can rule Method.invoke() now out as cause of my simulator onUpdate issue, it is still there after I removed all Method.invoke() calls.

  • To use this example:

    I now ran your example, and it works fine also in my environment and with the device I use for testing.

    I have been playing with my code a bit but still not getting any closer. If I let the timers run out after few executions, the first onUpdate after that still shows the delay and then further updates run fine. As if the pipe is still clogged and once the first onUpdate has passed through the others can run freely.

    Still, on the watch the app runs without any problems.

    But I still need to find it, the delays are now in the 10-15 seconds range, and it is very cumbersome to test anything. Also I am worried that there is something hidden in there that may also affect the app on the watch in ways I cannot see yet.

  • What is also interesting:

    When onUpdate is triggered by WatchUi.requestUpdate() there is only one onUpdate() and the delay is after that.

    15.4.2025 21:18:32: TaskQueue: Executing task 1/9 ...
    15.4.2025 21:18:32: EvccRequestUpdateTask: executing requestUpdate
    15.4.2025 21:18:32: WidgetSiteBase: onUpdate main site=0
    15.4.2025 21:18:32: WidgetSiteBase: onUpdate completed main site=0
    15.4.2025 21:18:43: TaskQueue: Executing task 1/8 ...

    When onUpdate is triggered by a WatchUi.switchToView() then there are two calls to onUpdate(), and the delay is between those two calls.

    15.4.2025 21:13:33: TaskQueue: Executing task 1/12 ...
    15.4.2025 21:13:33: ViewCarouselDelegate: onNextPage
    15.4.2025 21:13:33: WidgetSiteBase: onUpdate main site=2
    15.4.2025 21:13:33: WidgetSiteBase: onUpdate completed main site=2
    15.4.2025 21:13:41: WidgetSiteBase: onUpdate main site=2
    15.4.2025 21:13:41: WidgetSiteBase: onUpdate completed main site=2
    15.4.2025 21:13:41: TaskQueue: Executing task 1/11 ...

    As far as I have seen these two onUpdate() calls are atomic, there is no way to sneak app code between them. I think this shows that something of what I am doing with my timers and web requests is tripping up something in the (simulator) code.