Async/Await
UI Freezing
JavaScript
Performance Issues
Asynchronous Programming

UI still freezing when using async/await

Data Structures & Algorithms practice on Codemia

Step through 300 algorithm problems with animated visualisers that show the data structure changing as the code runs.

Practice algorithms

Introduction

Using async and await does not automatically make the UI responsive. In JavaScript, the UI still freezes whenever the main thread is busy doing synchronous work, even if that work happens inside an async function.

Why async and await Are Not Magic

await only pauses until a promise settles. It does not move CPU-heavy code to another thread.

This function is asynchronous:

javascript
1async function loadData() {
2  const response = await fetch("/api/data");
3  return response.json();
4}

While the network request is pending, the browser can keep rendering the UI. That part is good.

But this function still blocks the UI:

javascript
1async function freezeUi() {
2  let total = 0;
3
4  for (let i = 0; i < 1_000_000_000; i++) {
5    total += i;
6  }
7
8  return total;
9}

Even though the function is marked async, the loop is still synchronous CPU work on the main thread. The browser cannot paint or handle user input until that loop finishes.

Identify the Real Cause

UI freezing usually comes from one of these:

  • CPU-heavy loops
  • too much DOM work in one turn
  • synchronous parsing or transformation of large data
  • serial await usage that stretches user-perceived latency

The first category is the most common misunderstanding. async helps with waiting. It does not help with heavy computation unless you explicitly restructure that computation.

Break Work into Chunks

If the job can stay on the main thread, split it into smaller pieces so the browser can breathe between them.

javascript
1async function processItems(items) {
2  for (let i = 0; i < items.length; i++) {
3    doExpensiveWork(items[i]);
4
5    if (i % 100 === 0) {
6      await new Promise((resolve) => setTimeout(resolve, 0));
7    }
8  }
9}

This yields control back to the event loop periodically, which lets rendering and input handling continue.

It is not the same as true background execution, but it can make a dramatic difference in responsiveness.

Use Web Workers for Real CPU Work

If the work is genuinely heavy, the better fix is to move it off the main thread entirely.

Main thread:

javascript
1const worker = new Worker("worker.js");
2
3worker.onmessage = (event) => {
4  console.log("Result:", event.data);
5};
6
7worker.postMessage([1, 2, 3, 4, 5]);

Worker:

javascript
1self.onmessage = (event) => {
2  const result = event.data.reduce((sum, n) => sum + n, 0);
3  self.postMessage(result);
4};

This is the right model when the problem is computation, not waiting.

Avoid Unnecessary Sequential await

Sometimes the UI feels frozen because work is serialized unnecessarily:

javascript
const a = await fetch("/a");
const b = await fetch("/b");
const c = await fetch("/c");

If the requests are independent, run them together:

javascript
1const [a, b, c] = await Promise.all([
2  fetch("/a"),
3  fetch("/b"),
4  fetch("/c"),
5]);

This does not solve main-thread CPU blocking, but it does reduce wasted waiting time and can make the interface feel much snappier.

DOM Work Can Also Freeze the UI

Even without heavy calculations, a huge burst of DOM creation or layout-triggering updates can lock the UI for noticeable time. In that case, reduce reflows, batch updates, or virtualize long lists rather than focusing only on the async syntax.

Common Pitfalls

  • Assuming async means "runs on another thread". In JavaScript, it usually does not.
  • Putting CPU-intensive loops inside an async function and expecting the UI to stay smooth.
  • Using await inside loops when the tasks could run concurrently.
  • Forgetting that large DOM updates can freeze the UI even without network calls.
  • Using zero-delay timers as a universal fix when the real answer should be a Web Worker.

Summary

  • 'async and await help with waiting on promises, not with heavy synchronous computation.'
  • The UI freezes when the main thread is busy, regardless of whether the function is marked async.
  • Break long work into chunks if it must stay on the main thread.
  • Use Web Workers for real CPU-heavy processing.
  • Also watch for sequential awaits and expensive DOM updates, because both can make the interface feel stuck.

Related reading
Course
Intermediate
27 lessons
15 hours
DSA Fundamentals

Master algorithmic patterns and data structures through hands-on LeetCode-style problems - from arrays and hashing to dynamic programming and advanced graphs.

View the course
Track what you have practised

A free account saves your progress, solutions and study plan across every problem on Codemia.

Data Structures & Algorithms practice on Codemia

Step through 300 algorithm problems with animated visualisers that show the data structure changing as the code runs.

Practice algorithms

All Rights Reserved.