Categories
Web Development

JS Array Methods Cheatsheet

Handling collections of data efficiently is a core requirement of modern JavaScript engineering. While standard loops work, ECMAScript array methods provide highly expressive, declarative ways to mutate, filter, and transform datasets.

This reference handbook covers the most vital JavaScript array methods you need for clean production-grade code, broken down by execution type.

1. The Heavy Hitters (Transformation & Filtering)

These methods are non-mutating; they return a brand-new array without altering the original dataset configuration.

.map()

Transforms every single element inside an array and returns a new array of identical length.

JavaScript

const numbers = [1, 2, 3];
const doubled = numbers.map(num => num * 2); // [2, 4, 6]

.filter()

Evaluates every element against a conditional test. Returns a new array containing only elements that evaluate to true.

JavaScript

const scores = [45, 80, 92, 60];
const passing = scores.filter(score => score >= 70); // [80, 92]

.reduce()

Executes a user-supplied callback accumulator function across all elements, reducing the array down to a single output value (e.g., an object, string, or number).

JavaScript

const expenses = [100, 250, 50];
const total = expenses.reduce((acc, current) => acc + current, 0); // 400

2. Search & Verification Utilities

Methods designed to find specific values or verify structural criteria inside an array.

.find()

Returns the first item that matches your specified conditional criteria. If no matching block is found, it returns undefined.

JavaScript

const users = [{id: 1, name: 'Alice'}, {id: 2, name: 'Bob'}];
const targetUser = users.find(user => user.id === 2); // {id: 2, name: 'Bob'}

.includes()

Returns a simple boolean (true/false) stating whether a target primitive element exists within the collection.

JavaScript

const techStack = ['React', 'Node', 'MongoDB'];
const hasNextJS = techStack.includes('Next.js'); // false

3. Structural Mutators (Alters the Original Array)

Warning: These methods modify the original array in-place. Use with caution in state-managed applications like React.

  • .push(): Appends an element to the absolute end of the array.
  • .pop(): Removes the very last element of the array and returns it.
  • .shift(): Strips the first element out of the array layout.
  • .unshift(): Prepends a new item directly to the front of the array index.
    What is the difference between .map() and .forEach() in JavaScript?

    The .map() method transforms elements and returns an entirely new array of the same length without altering the source data. The .forEach() method simply executes a callback loop function on each item in-place and returns undefined.

    Does JavaScript .filter() modify the original array?

    No, .filter() is an immutable array method. It creates a shallow copy of a portion of the given array, filtered down to just the elements that pass the structural condition test.

    Categories
    React & Frontend

    React useEffect Hook: Complete Guide to Dependencies and Cleanup

    The useEffect hook is arguably the most powerful, yet most widely misunderstood tool in a React developer’s arsenal. It allows functional components to synchronize with external systems like APIs, subscriptions, and the browser DOM.

    However, misconfiguring your dependency array or forgetting a cleanup function can instantly introduce severe memory leaks or infinite re-render loops that crash the browser.

    In this comprehensive guide, we will break down exactly how to control the useEffect execution cycle safely.

    1. The Anatomy of useEffect

    The hook accepts two arguments: a callback function containing your side-effect logic, and an optional dependency array that dictates when the effect should re-run.

    JavaScript

    import { useEffect } from 'react';
    
    useEffect(() => {
        // 1. Setup Phase: Your side effect logic runs here (e.g., fetch data)
        
        return () => {
            // 2. Cleanup Phase: Runs before the component unmounts 
            // OR before the effect runs again.
        };
    }, [/* 3. Dependency Array */]);
    

    2. Mastering the Dependency Array

    The dependency array is where 90% of React bugs originate. It tells React when to skip applying an effect.

    • No Array undefined: The effect runs after every single render. Use this extremely sparingly, as updating state inside it will cause an infinite loop.
    • Empty Array []: The effect runs only once after the initial mount. Perfect for fetching initial page data or adding a global window event listener.
    • Populated Array [stateA, propB]: The effect runs on mount, and then only re-runs if stateA or propB have changed since the last render.

    3. Preventing Memory Leaks with Cleanup Functions

    If your effect creates a continuous process—like a setInterval, a WebSocket connection, or an event listener—you must return a cleanup function. If a user navigates away from the component without cleaning up, the process keeps running in the background, consuming memory.

    Here is the correct way to handle a window resize listener:

    JavaScript

    import { useState, useEffect } from 'react';
    
    export default function WindowSizeTracker() {
        const [width, setWidth] = useState(window.innerWidth);
    
        useEffect(() => {
            // Setup the listener
            const handleResize = () => setWidth(window.innerWidth);
            window.addEventListener('resize', handleResize);
    
            // Cleanup the listener when the component unmounts
            return () => {
                window.removeEventListener('resize', handleResize);
            };
        }, []); // Empty array ensures we only attach the listener once
    
        return <div>Current window width: {width}px</div>;
    }
    

    4. Fetching Data Safely

    When fetching data asynchronously inside useEffect, you cannot make the callback function itself async. Instead, declare an async function inside the effect and call it immediately.

    Furthermore, you should implement an AbortController in your cleanup function to cancel the fetch request if the user navigates away before the API responds.

    JavaScript

    useEffect(() => {
        const controller = new AbortController();
        
        const fetchUserData = async () => {
            try {
                const response = await fetch('/api/user', { signal: controller.signal });
                const data = await response.json();
                setUserData(data);
            } catch (error) {
                if (error.name !== 'AbortError') {
                    console.error("Fetch failed", error);
                }
            }
        };
    
        fetchUserData();
    
        // Abort the fetch if the component unmounts mid-request
        return () => controller.abort();
    }, []);
    Why is my React useEffect running infinitely?

    This happens when you update a state variable inside the useEffect, but you did not include a dependency array (or you included the updated state in the array). This triggers a re-render, which triggers the effect again, causing an infinite loop.

    Can I make the useEffect callback function async?

    No, React requires useEffect to either return nothing or return a synchronous cleanup function. To use async/await, define an asynchronous function inside the effect and invoke it immediately.

    What is the difference between useEffect and useLayoutEffect?

    useEffect runs asynchronously after the browser has painted the screen, making it non-blocking. useLayoutEffect runs synchronously before the browser paints. Only use useLayoutEffect if you need to measure DOM elements and mutate them before the user sees the screen.

    Categories
    Desktop Development DevOps & Cloud

    How to Fix UI Freezing in Electron Apps Using Node Worker Threads

    Building desktop applications with Electron offers incredible flexibility, but it comes with a common architecture trap. Because Electron’s Main and Renderer processes run on single threads, executing a heavy CPU-intensive operation—like processing large local files, cryptographic hashing, or managing massive JSON parsing—will instantly freeze your application’s user interface.

    Users see a stalled window or an “Application Not Responding” warning. To keep your desktop application running at a fluid 60 FPS, you must offload heavy background operations to dedicated Node.js Worker Threads.

    In this guide, we will implement a multi-threaded system inside an Electron utility layout.

    1. Why IPC Alone Doesn’t Fix UI Freezing

    Many developers assume that passing a heavy task from the Renderer process to the Main process via ipcMain and ipcRenderer solves performance lag. It does not. The Main process handles your application’s lifecycle and window rendering. If you clog the Main process loop, your entire application still locks up.

    Instead, you must spawn a completely separate background thread using the native Node.js worker_threads module.

    2. Writing the Background Worker Script

    First, create a separate file in your project named worker.js. This script will execute the heavy math or parsing safely away from your primary interface threads.

    JavaScript

    const { parentPort, workerData } = require('worker_threads');
    
    // Simulate a heavy computational CPU task
    function performHeavyCalculation(iterations) {
        let count = 0;
        for (let i = 0; i < iterations; i++) {
            count += Math.sqrt(i);
        }
        return count;
    }
    
    // Receive processing instructions from the main application thread
    const result = performHeavyCalculation(workerData.iterations);
    
    // Post the final computed data back to the parent thread
    parentPort.postMessage({ success: true, data: result });
    

    3. Spawning the Worker Thread from Electron Main

    Now, update your primary Electron main.js file to safely initialize this background thread when requested by an internal event hook.

    JavaScript

    const { app, BrowserWindow, ipcMain } = require('electron');
    const { Worker } = require('worker_threads');
    const path = require('path');
    
    let mainWindow;
    
    function createWindow() {
        mainWindow = new BrowserWindow({
            width: 800,
            height: 600,
            webPreferences: {
                nodeIntegration: false,
                contextIsolation: true,
                preload: path.join(__dirname, 'preload.js')
            }
        });
        mainWindow.loadFile('index.html');
    }
    
    // IPC Listener that handles processing requests without freezing the UI
    ipcMain.handle('trigger-heavy-task', async (event, iterationsCount) => {
        return new Promise((resolve, reject) => {
            // Spawn the background thread script
            const worker = new Worker(path.join(__dirname, 'worker.js'), {
                workerData: { iterations: iterationsCount }
            });
    
            // Listen for data responses from the worker thread
            worker.on('message', (message) => resolve(message));
            
            // Catch runtime execution execution failures safely
            worker.on('error', (err) => reject(err));
            
            worker.on('exit', (code) => {
                if (code !== 0) reject(new Error(`Worker stopped with exit code ${code}`));
            });
        });
    });
    
    app.whenReady().then(createWindow);
    
    Does async/await prevent UI freezing in Electron?

    No. Standard asynchronous JavaScript functions (like Promises or async/await syntax) still execute sequentially on the single main main thread. They prevent network blocking but cannot stop high-CPU mathematical loops from locking your app.

    Can I spawn a Node Worker Thread directly inside the Renderer process?

    No, the Renderer process operates within a Chromium context. To safely use multi-threading, you should handle process delegation within the Main Electron execution loop using secure context-isolated IPC channels.

    What is the memory overhead of spawning Node worker threads?

    Each unique Node.js worker thread instances its own V8 execution environment, which adds a lightweight memory baseline (roughly 10-30MB). It should be reserved specifically for heavy bulk operations rather than small tasks.

    Categories
    AI & API Development

    How to Stream OpenAI API Responses in Next.js Without Serverless Timeouts

    Building an AI chat application with Next.js can quickly become problematic when utilizing serverless deployments like Vercel or AWS Lambda. Standard HTTP requests wait for the entire payload to generate. If an AI response takes longer than 15 seconds, your backend execution caps out and returns an abrupt 504 Timeout Error.

    To fix this, you must shift from traditional JSON payloads to Server-Sent Events (SSE) or utilize the modern Vercel AI SDK to stream text chunks iteratively to the client interface as soon as they are processed by the LLM.

    In this guide, we will implement a clean architecture to handle streaming effortlessly.

    1. The Route Handler (Backend Stream Setup)

    Instead of returning a standard NextResponse.json(), we configure our Next.js App Router handler to return a custom ReadableStream with a text/event-stream header.

    Create or update your route file (app/api/chat/route.js):

    JavaScript

    import OpenAI from 'openai';
    
    const openai = new OpenAI({ apiKey: process.env.OPENAI_API_KEY });
    
    export const runtime = 'edge'; // Optional: Use Edge runtime for zero cold-start times
    
    export async function POST(req) {
        const { messages } = await req.json();
    
        // Trigger a streaming completion from OpenAI
        const response = await openai.chat.completions.create({
            model: 'gpt-4o-mini',
            stream: true,
            messages,
        });
    
        // Transform the OpenAI stream into an HTTP response stream
        const stream = new ReadableStream({
            async start(controller) {
                const encoder = new TextEncoder();
                for await (const chunk of response) {
                    const text = chunk.choices[0]?.delta?.content || '';
                    if (text) {
                        controller.enqueue(encoder.encode(text));
                    }
                }
                controller.close();
            },
        });
    
        return new Response(stream, {
            headers: {
                'Content-Type': 'text/event-stream; charset=utf-8',
                'Cache-Control': 'no-cache, no-transform',
                'Connection': 'keep-alive',
            },
        });
    }
    

    2. Consuming the Stream on the Frontend Component

    On the client side, we cannot use a simple await response.json(). Instead, we must read from the response’s body stream reader loop by looping through the chunks dynamically.

    JavaScript

    'use client';
    import { useState } from 'react';
    
    export default function ChatComponent() {
        const [input, setInput] = useState('');
        const [chatLog, setChatLog] = useState('');
    
        const handleSubmit = async (e) => {
            e.preventDefault();
            setChatLog(''); // Clear and prepare log
    
            const response = await fetch('/api/chat', {
                method: 'POST',
                body: JSON.stringify({ messages: [{ role: 'user', content: input }] }),
            });
    
            const reader = response.body.getReader();
            const decoder = new TextDecoder();
            let done = false;
    
            while (!done) {
                const { value, done: doneReading } = await reader.read();
                done = doneReading;
                const chunkValue = decoder.decode(value);
                // Append incoming tokens to state dynamically for the typing effect
                setChatLog((prev) => prev + chunkValue);
            }
        };
    
        return (
            <div className="p-4 max-w-xl mx-auto">
                <form onSubmit={handleSubmit} className="flex gap-2 mb-4">
                    <input 
                        value={input} 
                        onChange={(e) => setInput(e.target.value)}
                        className="border p-2 w-full rounded" 
                        placeholder="Ask something..."
                    />
                    <button type="submit" className="bg-blue-600 text-white p-2 rounded">Send</button>
                </form>
                <div className="bg-gray-100 p-4 rounded min-h-[150px] whitespace-pre-wrap">
                    {chatLog || "AI response will stream here..."}
                </div>
            </div>
        );
    }
    
    Why does my OpenAI request timeout on Vercel?

    Standard Vercel serverless functions have a strict execution time limit (10–15 seconds on hobby tiers). Because deep AI completions can take longer to fully generate, waiting for a full JSON response triggers a 504 Gateway Timeout.

    What does ‘export const runtime = edge’ do for AI streaming?

    Switching the Next.js execution runtime to the Edge network bypasses traditional serverless timeout limitations entirely and removes cold start delays, making it ideal for processing long, multi-second LLM streaming streams.

    Can I use standard Axios to parse a server-sent stream?

    While classic Axios configuration can support streaming, using native fetch() alongside browser stream readers (ReadableStreamDefaultReader) is much more straightforward and lightweight for handling token typing animations on frontend components.

    Categories
    AI & API Development Solutions Web

    How to Fix OpenAI API Rate Limit Error 429 and Timeouts: A Developer’s Guide

    If you are building an AI-powered application using the OpenAI SDK or Anthropic Claude API, encountering the dreaded Error 429: Rate Limit Exceeded or sudden connection timeouts is a rite of passage.

    This usually happens when your application triggers too many Requests Per Minute (RPM), consumes too many Tokens Per Minute (TPM), or fails to manage asynchronous requests properly inside serverless server environments like Next.js App Router or AWS Lambda.

    In this guide, we will implement the industry-standard software engineering patterns to handle these API errors gracefully.

    1. The Right Way: Implementing Exponential Backoff with Jitter

    The absolute worst way to handle a 429 error is to immediately retry the request in a tight loop. This will quickly get your server’s IP temporarily flagged. Instead, use exponential backoff, which spaces out retries exponentially, combined with random “jitter” to avoid hammering the API endpoints concurrently.

    Node.js / JavaScript Example (Using a custom loop or retry library):

    JavaScript

    async function fetchAIResponseWithRetry(prompt, retries = 3, delay = 1000) {
        try {
            const response = await openai.chat.completions.create({
                model: "gpt-4o-mini",
                messages: [{ role: "user", content: prompt }],
            });
            return response;
        } catch (error) {
            if (error.status === 429 && retries > 0) {
                // Calculate backoff with a bit of randomness (jitter)
                const jitter = Math.random() * 200;
                const nextDelay = delay * 2 + jitter;
                
                console.warn(`Rate limited. Retrying in ${nextDelay.toFixed(0)}ms...`);
                await new Promise(resolve => setTimeout(resolve, nextDelay));
                
                return fetchAIResponseWithRetry(prompt, retries - 1, nextDelay);
            }
            throw error; // Pass the error along if retries run out
        }
    }
    

    2. Upgrading to the Modern SDK In-Built Retries

    Many developers on Reddit are unaware that the latest versions of the official OpenAI SDKs actually have built-in retry mechanisms that you can configure during instantiation. This significantly cleans up your codebase.

    Configuring built-in retries in Node.js:

    JavaScript

    import OpenAI from 'openai';
    
    const openai = new OpenAI({
      apiKey: process.env.OPENAI_API_KEY,
      maxRetries: 5, // Automatically defaults to 2, but you can increase it for heavy bulk operations
      timeout: 20 * 1000, // 20 seconds timeout limit
    });
    

    3. Handling Token Budgets via Tokenizers

    If you are hitting TPM (Tokens Per Minute) ceilings, you need to count your tokens locally before making the API call. If a prompt or context window is too large, drop or truncate older system context.

    In Python, use the tiktoken library, or in JS, use js-tiktoken to estimate costs locally:

    JavaScript

    import { getEncoding } from "js-tiktoken";
    const enc = getEncoding("cl100k_base");
    
    const tokenCount = enc.encode("Your long prompt string goes here").length;
    console.log(`Estimated prompt tokens: ${tokenCount}`);
    Why am I hitting OpenAI Error 429 even though I have credits?

    OpenAI splits accounts into Tier levels based on your payment history, not just your current balance. New accounts are capped at low Requests Per Minute (RPM) thresholds. Check your Limits dashboard in the OpenAI developer console to see your specific Tier tier constraints.

    What is the difference between RPM and TPM rate limits?

    RPM stands for Requests Per Minute, which counts the raw number of API calls you send. TPM stands for Tokens Per Minute, which counts the total text volume (prompt + generated response tokens) processed. Hitting either threshold will trigger a 429 error.

    How do I prevent Vercel or AWS serverless timeouts with OpenAI?

    Standard serverless functions usually have a 10 to 15-second execution timeout, while large AI models can take longer to reply. To fix this, you must configure your API call to stream responses using ‘stream: true’ and process chunked text iteratively on the client-side.