≡ Menu

If you’ve ever seen a “Maximum call stack size exceeded” error in your console, you’ve met the Call Stack.

But what is it exactly, and how does it handle complex tasks without freezing your browser?

To understand JavaScript performance, you have to look past the syntax and into the engine. Let’s break down the two most important pieces of the puzzle: the Call Stack and the Event Loop.


1. What is the Call Stack?

In simple terms, the Call Stack is the mechanism JavaScript uses to keep track of its place in a script. It tells the engine what function is currently running and what functions are called from within that function.

How It Works: LIFO

The Call Stack operates on a LIFO (Last In, First Out) principle. Imagine a stack of physical books:

  • You add a book to the top (Push).

  • You can only remove the book that is currently on the top (Pop).

The Lifecycle of a Function Call

When you run a JavaScript file, the engine creates a Global Execution Context. As it encounters functions, it follows these steps:

  1. Push: When a function is invoked, it is added to the top of the stack.

  2. Execute: The engine runs the code inside that function.

  3. Pop: Once the function returns a value or finishes, it is removed from the stack.


2. A Real-World Example

Let’s look at how the stack handles this snippet:

function greet() {
  sayHi();
  console.log("Back in greet");
}

function sayHi() {
  console.log("Hi!");
}

greet();

The Execution Flow:

  1. greet() is called => Added to stack.

  2. Inside greet, sayHi() is called => Added to top of stack.

  3. sayHi executes console.log("Hi!").

  4. sayHi finishes => Popped off the stack.

  5. Control returns to greet, which executes console.log("Back in greet").

  6. greet finishes => Popped off the stack.


3. The “Single-Threaded” Limitation

Because there is only one Call Stack, JavaScript is single-threaded.

It can only do one thing at a time.

If a function at the top of the stack takes a long time to run (like a massive calculation), it “blocks” the stack.

This is why a website might feel “frozen”—the Call Stack is busy and can’t handle your clicks.

What is a Stack Overflow?

A stack overflow happens when a function calls itself (recursion) without an exit strategy.

The stack grows until it hits the browser’s memory limit.

function recurse() {
  recurse(); 
}
recurse(); // Uncaught RangeError: Maximum call stack size exceeded

4. The Event Loop: Handling Asynchronous Magic

If JavaScript can only do one thing at a time, how does it handle network requests (fetch) or timers without freezing?

This is where the Event Loop, Web APIs, and the Callback Queue come in.

Step 1: Web APIs

When you call an asynchronous function like setTimeout, JavaScript doesn’t wait for it. It hands the task to the browser’s Web APIs and moves to the next line of code immediately.

Step 2: The Callback Queue

Once the Web API finishes its task (e.g., the 2-second timer ends), the callback function is sent to the Callback Queue. It sits there, waiting for its turn to run.

Step 3: The Event Loop

The Event Loop is a constant monitor. It has one specific rule:

“If the Call Stack is empty, take the first task from the Callback Queue and push it onto the Stack.”

Example in Action:

console.log("Start");

setTimeout(() => {
  console.log("Callback");
}, 0);

console.log("End");

Output:

  1. Start

  2. End

  3. Callback

Even with a 0ms delay, “Callback” prints last. Why? Because the Event Loop had to wait for the Call Stack to be completely empty (after “End” was logged) before it could move the callback from the queue to the stack.


Summary

  • Call Stack: Manages the order of execution (LIFO).

  • Single-Threaded: JavaScript handles one task at a time.

  • Web APIs: Handle heavy lifting (timers, requests) outside the main thread.

  • Event Loop: Coordinates between the stack and the queue to keep things running smoothly.

Useful links below:

Let me & my team build you a money making website/blog for your business https://bit.ly/tnrwebsite_service

Get Bluehost hosting for as little as $1.99/month (save 75%)…https://bit.ly/3C1fZd2

Best email marketing automation solution on the market! http://www.aweber.com/?373860

Build high converting sales funnels with a few simple clicks of your mouse! https://bit.ly/484YV29

Join my Patreon for one-on-one coaching and help with your coding…https://www.patreon.com/c/TyronneRatcliff

Buy me a coffee ☕️https://buymeacoffee.com/tyronneratcliff

 

{ 0 comments }

When building modern web applications, handling large datasets efficiently is a must-have skill. If you try to load 1,000 items at once, your page will lag, and your users will leave. The solution is Server-Side Pagination.

In this tutorial, we’ll build a robust pagination system that fetches data from a live API, handles dynamic button generation, and includes “smart” ellipsis (...) for a professional UI.


1. The Strategy: “The Sliding Window”

Instead of showing every page button, we use a “Sliding Window” logic. This keeps the UI clean by only showing:

  1. The First and Last pages.

  2. A small Range (2 pages) around the current page.

  3. Ellipses (…) to bridge the gaps.


2. The Full Implementation

Here is the complete, single-file solution. You can save this as index.html and run it immediately.

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Professional API Pagination</title>
    <style>
        :root { --primary: #007bff; --bg: #f4f7f6; --text: #333; }
        body { font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif; background: var(--bg); color: var(--text); padding: 40px 20px; max-width: 700px; margin: auto; }
        
        h2 { text-align: center; color: #444; }
        
        /* List Styling */
        #list { list-style: none; padding: 0; min-height: 400px; }
        .post-item { background: #fff; border: 1px solid #ddd; margin: 10px 0; padding: 15px; border-radius: 8px; box-shadow: 0 2px 4px rgba(0,0,0,0.05); transition: transform 0.2s; }
        .post-item:hover { transform: translateY(-2px); }
        .post-item h3 { margin: 0 0 8px 0; font-size: 1.1rem; color: var(--primary); }
        
        /* Controls Styling */
        .controls { display: flex; gap: 8px; justify-content: center; align-items: center; margin-top: 30px; flex-wrap: wrap; }
        button { padding: 8px 16px; border: 1px solid var(--primary); background: #fff; color: var(--primary); cursor: pointer; border-radius: 4px; font-weight: 600; }
        button:hover:not(:disabled) { background: var(--primary); color: white; }
        button.active { background: var(--primary); color: white; }
        button:disabled { border-color: #ccc; color: #ccc; cursor: not-allowed; }
        .dots { color: #888; padding: 0 5px; font-weight: bold; }
        
        .loading { text-align: center; font-weight: bold; color: var(--primary); display: none; }
    </style>
</head>
<body>

    <h2>Published Posts</h2>
    <div id="loading" class="loading">Fetching Data...</div>
    <ul id="list"></ul>

    <div class="controls">
        <button id="prevBtn">Previous</button>
        <div id="page-numbers"></div>
        <button id="nextBtn">Next</button>
    </div>

    <script>
        // State Management
        let currentPage = 1;
        const itemsPerPage = 6;
        let totalPages = 0;

        // DOM Elements
        const listEl = document.getElementById('list');
        const pageNumbersEl = document.getElementById('page-numbers');
        const loadingEl = document.getElementById('loading');

        /**
         * Fetches data from API using page and limit parameters
         */
        async function fetchPosts(page) {
            loadingEl.style.display = 'block';
            listEl.style.opacity = '0.5';
            
            try {
                const response = await fetch(`https://jsonplaceholder.typicode.com/posts?_page=${page}&_limit=${itemsPerPage}`);
                const data = await response.json();

                // Calculate total pages from the API header
                const totalCount = response.headers.get('x-total-count');
                totalPages = Math.ceil(totalCount / itemsPerPage);

                renderPosts(data);
                renderButtons();
            } catch (error) {
                listEl.innerHTML = `<p style="color:red">Error loading data. Please try again.</p>`;
            } finally {
                loadingEl.style.display = 'none';
                listEl.style.opacity = '1';
            }
        }

        /**
         * Renders the list of posts
         */
        function renderPosts(posts) {
            listEl.innerHTML = posts.map(post => `
                <li class="post-item">
                    <h3>${post.id}. ${post.title.substring(0, 40)}...</h3>
                    <p>${post.body.substring(0, 80)}...</p>
                </li>
            `).join('');
        }

        /**
         * Renders the smart pagination buttons with ellipses
         */
        function renderButtons() {
            pageNumbersEl.innerHTML = '';
            const range = 1; // How many pages to show around the current page
            let lastRenderedPage = 0;

            for (let i = 1; i <= totalPages; i++) {
                // Logic: Always show first, last, and the range around current page
                const isFirstOrLast = i === 1 || i === totalPages;
                const isWithinRange = i >= currentPage - range && i <= currentPage + range;

                if (isFirstOrLast || isWithinRange) {
                    
                    // If there's a gap, add dots
                    if (lastRenderedPage && i - lastRenderedPage > 1) {
                        const dots = document.createElement('span');
                        dots.innerText = '...';
                        dots.className = 'dots';
                        pageNumbersEl.appendChild(dots);
                    }

                    const btn = document.createElement('button');
                    btn.innerText = i;
                    btn.className = i === currentPage ? 'active' : '';
                    btn.onclick = () => {
                        currentPage = i;
                        fetchPosts(i);
                        window.scrollTo({ top: 0, behavior: 'smooth' });
                    };

                    pageNumbersEl.appendChild(btn);
                    lastRenderedPage = i;
                }
            }

            // Update disabled state for arrows
            document.getElementById('prevBtn').disabled = currentPage === 1;
            document.getElementById('nextBtn').disabled = currentPage === totalPages;
        }

        // Arrow Button Listeners
        document.getElementById('prevBtn').onclick = () => {
            if (currentPage > 1) fetchPosts(--currentPage);
        };

        document.getElementById('nextBtn').onclick = () => {
            if (currentPage < totalPages) fetchPosts(++currentPage);
        };

        // Initial Load
        fetchPosts(currentPage);
    </script>
</body>
</html>

3. How the “Smart Dots” Logic Works

The magic happens in the renderButtons function. We track the lastRenderedPage. If the current page i we are about to draw is more than 1 step away from the lastRenderedPage, we know there is a gap.

For example, if we just drew page 1 and the next page the logic allows is page 8, the script sees that 8 - 1 > 1 and injects the ... span automatically.


4. Key Takeaways

  • Performance: We only fetch 6 items at a time, making the app incredibly light.

  • Scalability: This code works just as well for 1,000 pages as it does for 5.

  • User Experience: We included window.scrollTo so that when a user clicks a page, they are taken back to the top of the list.

Useful links below:

Let me & my team build you a money making website/blog for your business https://bit.ly/tnrwebsite_service

Get Bluehost hosting for as little as $1.99/month (save 75%)…https://bit.ly/3C1fZd2

Best email marketing automation solution on the market! http://www.aweber.com/?373860

Build high converting sales funnels with a few simple clicks of your mouse! https://bit.ly/484YV29

Join my Patreon for one-on-one coaching and help with your coding…https://www.patreon.com/c/TyronneRatcliff

Buy me a coffee ☕️https://buymeacoffee.com/tyronneratcliff

{ 0 comments }