Delayed onReceive Callback and UI Blocking After Web Request with Large JSON

After recently dealing with Storage limits when handling large JSON responses from web requests, I’ve encountered another related issue.

When sending a request using Communications.makeWebRequest, the app continues to process other events until the response is received. In my case, these requests are used to update already displayed data, and the user can interact with the app while updates are processed. To avoid blocking the UI, I handle the response inside a task queue, which chunks the processing of the large JSON—this part works well on my end.

However, I’ve observed a problem with the processing that occurs before my callback is triggered. In most cases, even with large (but within-limit) JSON responses, the pre-callback processing is fast enough not to affect the user experience. But occasionally, the delay before the callback is invoked spikes to 4–5 seconds, during which the UI becomes unresponsive.

Here’s a log excerpt from one instance where this occurs:

28.5.2025 15:01:14: TaskQueue: start executing tasks (12 queued)
28.5.2025 15:01:14: SitemapRequest.makeRequest (#5)
28.5.2025 15:01:14: TaskQueue: stop executing tasks after 46ms (0 remaining)
28.5.2025 15:01:18: SitemapRequest.onReceive: start (#5)
28.5.2025 15:01:18: SitemapRequest.onReceive: end

There is a 4-second delay between makeRequest and the start of onReceive, which is not typical. The request is sent to a local server, so network latency is not a factor. Also, if the delay were due to a slow response, I would expect the app not to block.

For comparison, here’s a log from a typical case—same server, same JSON response:

29.5.2025 11:25:47: TaskQueue: start executing tasks (3 queued)
29.5.2025 11:25:47: SitemapRequest.makeRequest (#7)
29.5.2025 11:25:47: TaskQueue: stop executing tasks after 62ms (0 remaining)
29.5.2025 11:25:47: SitemapRequest.onReceive: start (#7)
29.5.2025 11:25:47: SitemapRequest.onReceive: end

I’m trying to understand what might be causing the API’s internal processing—between the network response and the invocation of the callback—to occasionally take significantly longer, blocking the app during that time.

Has anyone else experienced similar issues with large JSON responses and delayed callbacks in makeWebRequest? Any insights or workarounds would be appreciated.

  • Here’s some test data collected from the watch. It comes from two separate runs, as you can tell from the timestamps. In the first run, the delay appeared as early as the third request. In the second run, it took a bit longer to reproduce, which suggests the issue occurs irregularly.

    Typically, the pre-processing time falls somewhere between 150ms and 300ms, but occasionally it spikes dramatically—up to around 7000ms.

    If you're interested in the code, you can find it here:
    https://github.com/TheNinth7/ohg/blob/onReceiveDelayTest/source/web-requests/sitemap/SitemapRequest.mc
    Take a look at the makeWebRequest, onReceive, and TimeMeasurerTask functions. For scheduling, I'm using a task queue—the main purpose of which is to break up the processing of responses into smaller chunks, so it doesn't block the main thread for too long at once.

    This also brings me to what I’d expect from Garmin: if pre-processing the response occasionally takes longer for some reason, it should be handled in a non-blocking way—so that the app can continue processing user input during that time.

    31.5.2025 7:52:45: SitemapRequest.makeRequest: #1
    31.5.2025 7:52:49: SitemapRequest.onReceive: #1
    31.5.2025 7:52:49: SitemapRequest.onReceive: total=4645ms pre-processing=188ms
    31.5.2025 7:52:51: SitemapRequest.makeRequest: #2
    31.5.2025 7:52:51: SitemapRequest.onReceive: end
    31.5.2025 7:52:52: SitemapRequest.onReceive: #2
    31.5.2025 7:52:52: SitemapRequest.onReceive: total=1070ms pre-processing=148ms
    31.5.2025 7:52:52: SitemapRequest.onReceive: end
    31.5.2025 7:53:11: SitemapRequest.makeRequest: #3
    31.5.2025 7:53:17: SitemapRequest.onReceive: #3
    31.5.2025 7:53:17: SitemapRequest.onReceive: total=6643ms pre-processing=5262ms
    31.5.2025 7:53:17: SitemapRequest.onReceive: end
    31.5.2025 7:53:18: HomepageMenuDelegate.onBack
    31.5.2025 7:56:49: SitemapRequest.makeRequest: #1
    31.5.2025 7:56:50: SitemapRequest.onReceive: #1
    31.5.2025 7:56:50: SitemapRequest.onReceive: total=1025ms pre-processing=171ms
    31.5.2025 7:56:52: SitemapRequest.makeRequest: #2
    31.5.2025 7:56:52: SitemapRequest.onReceive: end
    31.5.2025 7:56:53: SitemapRequest.onReceive: #2
    31.5.2025 7:56:53: SitemapRequest.onReceive: total=1080ms pre-processing=151ms
    31.5.2025 7:56:53: SitemapRequest.onReceive: end
    31.5.2025 7:57:2: SitemapRequest.makeRequest: #3
    31.5.2025 7:57:9: SitemapRequest.onReceive: #3
    31.5.2025 7:57:9: SitemapRequest.onReceive: total=7964ms pre-processing=7093ms
    31.5.2025 7:57:10: SitemapRequest.onReceive: end
    31.5.2025 7:57:17: SitemapRequest.makeRequest: #4
    31.5.2025 7:57:19: SitemapRequest.onReceive: #4
    31.5.2025 7:57:19: SitemapRequest.onReceive: total=1711ms pre-processing=302ms
    31.5.2025 7:57:19: SitemapRequest.onReceive: end
    31.5.2025 7:57:37: SitemapRequest.makeRequest: #5
    31.5.2025 7:57:39: SitemapRequest.onReceive: #5
    31.5.2025 7:57:39: SitemapRequest.onReceive: total=1960ms pre-processing=157ms
    31.5.2025 7:57:39: SitemapRequest.onReceive: end
    31.5.2025 7:57:58: SitemapRequest.makeRequest: #6
    31.5.2025 7:58:0: SitemapRequest.onReceive: #6
    31.5.2025 7:58:0: SitemapRequest.onReceive: total=1990ms pre-processing=212ms
    31.5.2025 7:58:0: SitemapRequest.onReceive: end
    31.5.2025 7:58:20: SitemapRequest.makeRequest: #7
    31.5.2025 7:58:21: SitemapRequest.onReceive: #7
    31.5.2025 7:58:21: SitemapRequest.onReceive: total=1696ms pre-processing=148ms
    31.5.2025 7:58:21: SitemapRequest.onReceive: end
    31.5.2025 7:58:45: SitemapRequest.makeRequest: #8
    31.5.2025 7:58:48: SitemapRequest.onReceive: #8
    31.5.2025 7:58:48: SitemapRequest.onReceive: total=3280ms pre-processing=1238ms
    31.5.2025 7:58:48: SitemapRequest.onReceive: end
    31.5.2025 7:58:49: HomepageMenuDelegate.onBack

    You might also notice that the initial request sometimes takes a very long time—I’ve observed delays of up to 28 seconds. Again, this doesn’t happen consistently. Importantly, this time isn’t spent in the pre-processing phase, and it doesn’t block the user interface, so I believe it’s a separate issue. My guess is that it might be related to initializing the communication channel to the phone that's required to make the request—but that’s purely speculative at this point.

  • Ok, this is an interesting way. I agree with it. Until we get better clues from Garmin (btw it might be different on different devices) this is our best guess

  • Brainstorming:

    Maybe you could save the huge response to storage, and then you could read it back. I assume that there is some deserislization involved, not necessarily the same as for the json response, but probably comparable.

    And if you also store and read back a similar size string then you can also estimate how much time is the I/O and assume the rest is the deserislization of the object.

    And then you can try to read this stored data again and again, to see if the sporadic increase also happens or not.

    If yes then it is caused by something unrelated to the web request

  • Maybe you could save the huge response to storage,

    Unfortunately, the response I'm testing with can't be written to storage. At a certain point, Storage.setValue fails with an OutOfMemory error—this happens even when there appears to be sufficient free memory. This failure occurs before makeWebRequest returns a -402 response code. See here for details: https://forums.garmin.com/developer/connect-iq/i/bug-reports/storage-setvalue-should-handle-memory-limits-gracefully

    That said, I can still run your test—just within the working size limit—and observe the results.