15 topics · Real examples · Zero setup

JavaScript, one concept at a time.

A complete practical guide with runnable examples for state, strings, logic, loops, functions, objects, DOM interactions, timers, async flows and APIs.

HTML HTML Guide CSS CSS Guide JS JavaScript Guide
📝 Variables 🔢 Data Types 🎯 Scope 🔤 Strings ❓ Conditions 🔁 Loops ⚙️ Functions 📎 Closures 📋 Arrays 📦 Objects 🏗️ Classes 🌐 DOM ⏰ Timers ⏳ Async 🔌 APIs

📝

Variables

Store and manage data in your programs.

Variables hold the state your app works with: the logged-in user, current theme, cart total, form status, active tab, or API response. In modern JavaScript, use const by default, switch to let only when a value really changes, and avoid var because it behaves in older, more error-prone ways.
App state values ▶ Try it
const userName = 'Adrian';
const planName = 'Pro';
let openProjects = 3;

openProjects = openProjects + 1;

console.log(`${userName} uses ${planName}`);
console.log('Open projects:', openProjects);
// → Adrian uses Pro
// → Open projects: 4
Naming for readability ▶ Try it
const userEmail = 'john@example.com';
const totalPrice = 99.99;
const isLoggedIn = true;
const lastLoginDate = '2026-04-10';

console.log(userEmail);
console.log(totalPrice);
console.log(isLoggedIn);
console.log(lastLoginDate);

// Compare this with vague names like data, value, item, temp
Reassign only when needed ▶ Try it
let buttonLabel = 'Save draft';
console.log(buttonLabel);

const hasChanges = true;
if (hasChanges) {
  buttonLabel = 'Publish changes';
}

console.log(buttonLabel); // → Publish changes
const with objects ▶ Try it
const project = {
  name: 'LiveJS',
  theme: 'Midnight',
  activeUsers: 1200
};

project.activeUsers = 1225;
project.theme = 'Sunrise';

console.log(project);
// const prevents reassignment of the variable itself, not changes inside the object

🎯

Scope

Where variables are accessible in your code.

Scope decides which parts of your code can see a value. That matters when you track UI state, handle events, validate forms, or split code into reusable functions. If scope is unclear, bugs appear fast. In modern JavaScript, the safest pattern is to keep values as local as possible and avoid leaking temporary state into the global area.
Global vs local app config ▶ Try it
const appName = 'LiveJS';

function renderHeader() {
  const pageTitle = 'Editor';

  console.log(appName); // → LiveJS
  console.log(pageTitle); // → Editor
}

renderHeader();
console.log(appName); // → LiveJS
// console.log(pageTitle); // Error: only exists inside renderHeader
Block scope in validation ▶ Try it
const email = 'team@example.com';

if (email.includes('@')) {
  const message = 'Email format looks correct.';
  console.log(message);
}

// console.log(message); // Error: message is scoped to the if block

if (true) {
  var legacyNote = 'var escapes the block';
}

console.log(legacyNote); // → var escapes the block
Nested scope chain ▶ Try it
const brand = 'LiveJS';

function openWorkspace() {
  const section = 'Projects';
  
  function openProject() {
    const projectName = 'Landing Revamp';
    console.log(brand, section, projectName);
    // → LiveJS Projects Landing Revamp
  }
  
  openProject();
}

openWorkspace();
Avoid accidental globals ▶ Try it
function startPreview() {
  const previewTheme = 'midnight';
  console.log('Preview theme:', previewTheme);
}

startPreview();

// Good: previewTheme stays local to the function
// Bad: assigning without let/const would leak a global value

🔢

Data Types

The different kinds of values JavaScript works with.

JavaScript works with primitive values like text, numbers, booleans, null, and undefined, plus reference types like arrays and objects. Knowing the type of a value matters constantly: form input arrives as strings, API data may contain null, booleans drive UI state, and converting values safely avoids subtle bugs.
Common UI values ▶ Try it
const pageTitle = 'Dashboard';      // string
const openTabs = 3;               // number
const isPreviewOpen = true;       // boolean
let selectedTheme;                    // undefined
const currentUser = null;         // null

console.log(pageTitle, openTabs, isPreviewOpen, selectedTheme, currentUser);
Inspect API values ▶ Try it
const response = {
  title: 'Launch checklist',
  views: 120,
  published: false,
  tags: ['release', 'ops']
};

console.log(typeof response.title); // → string
console.log(typeof response.views); // → number
console.log(typeof response.published); // → boolean
console.log(Array.isArray(response.tags)); // → true
Convert form input ▶ Try it
const quantityInput = '4';
const newsletterValue = 'true';

const quantity = Number(quantityInput);
const wantsNewsletter = newsletterValue === 'true';

console.log(quantity + 1); // → 5
console.log(wantsNewsletter); // → true
null vs undefined ▶ Try it
let draftTitle;
const selectedProject = null;

console.log(draftTitle); // → undefined
console.log(selectedProject); // → null

// undefined = not assigned yet
// null = intentionally empty

🔤

String Manipulation

Work with text: template literals, methods, and formatting.

Strings are everywhere in front-end work: button labels, URLs, search text, API messages, slugs, validation errors, and formatted UI copy. Beyond template literals, the useful day-to-day skills are trimming input, checking keywords, replacing parts of text, and turning raw text into something ready for display.
Dynamic UI message ▶ Try it
const firstName = 'Mara';
const unreadCount = 4;
const message = `Welcome back, ${firstName}. You have ${unreadCount} unread messages.`;

console.log(message);
// → Welcome back, Mara. You have 4 unread messages.
Clean user input ▶ Try it
const rawEmail = '   TEAM@EXAMPLE.COM   ';
const cleanEmail = rawEmail.trim().toLowerCase();

console.log(cleanEmail); // → team@example.com
console.log(cleanEmail.includes('@')); // → true
console.log(cleanEmail.endsWith('.com')); // → true
Build a slug ▶ Try it
const articleTitle = 'How to Build a Fast Landing Page';
const slug = articleTitle
  .toLowerCase()
  .replaceAll(' ', '-');

console.log(slug);
// → how-to-build-a-fast-landing-page
split, map & join ▶ Try it
const tags = 'html, css, javascript, performance';

const tagList = tags
  .split(',')
  .map(tag => tag.trim())
  .join(' | ');

console.log(tagList);
// → html | css | javascript | performance
Extract file extension ▶ Try it
const fileName = 'hero-background.webp';
const extension = fileName.slice(fileName.lastIndexOf('.') + 1);

console.log(extension); // → webp
console.log(fileName.startsWith('hero')); // → true

Conditions

Make decisions based on values with if, else, and switch.

Conditions drive almost every interaction in an app: whether to show a warning, enable a button, route a user, display a plan badge, or stop a form submit. The important part is not just syntax, but expressing business rules clearly so another developer can read the logic fast.
Pricing plan badge ▶ Try it
const projectsCount = 14;

if (projectsCount >= 20) {
  console.log('Enterprise plan');
} else if (projectsCount >= 10) {
  console.log('Pro plan');
} else if (projectsCount >= 1) {
  console.log('Starter plan');
} else {
  console.log('No active plan yet');
}
Publish permissions ▶ Try it
const isLoggedIn = true;
const hasVerifiedEmail = true;
const isSuspended = false;

if (isLoggedIn && hasVerifiedEmail && !isSuspended) {
  console.log('User can publish updates.');
}

const canPreview = isLoggedIn && (hasVerifiedEmail || !isSuspended);
console.log('Preview access:', canPreview);
// → Preview access: true
Ternary for labels ▶ Try it
const hasUnsavedChanges = false;
const buttonText = hasUnsavedChanges ? 'Save changes' : 'Saved';
const badgeTone = hasUnsavedChanges ? 'warning' : 'success';

console.log(buttonText);
console.log(badgeTone);
switch by route ▶ Try it
const route = '/settings';

switch (route) {
  case '/':
    console.log('Render home page');
    break;
  case '/editor':
    console.log('Render editor page');
    break;
  case '/settings':
    console.log('Render settings page');
    break;
  default:
    console.log('Render 404 page');
}

🔁

Loops

Repeat code with for, while, and for...of.

Loops are still essential when you need to render repeated UI, process imported data, retry a task, or scan a list for a match. Even if modern code often uses array methods, understanding plain loops helps you reason about counters, exits, and control flow without guessing.
Render pagination buttons ▶ Try it
let pagination = '';

for (let page = 1; page <= 5; page++) {
  pagination += `[${page}] `;
}

console.log(pagination.trim());
// → [1] [2] [3] [4] [5]
Retry until success ▶ Try it
let attempt = 1;
let connected = false;

while (!connected && attempt <= 3) {
  console.log(`Trying connection #${attempt}`);
  connected = attempt === 3;
  attempt++;
}

console.log('Connected:', connected);
// → Connected: true
Process notifications ▶ Try it
const notifications = [
  'New comment',
  'Build completed',
  'Payment received'
];

for (const notification of notifications) {
  console.log(`Inbox item: ${notification}`);
}

notifications.forEach((notification, index) => {
  console.log(`${index + 1}. ${notification}`);
});
// → 1. New comment
// → 2. Build completed
// → 3. Payment received
break & continue in validation ▶ Try it
const fields = ['email', '', 'password', 'timezone'];

for (const field of fields) {
  if (!field) {
    console.log('Skipping empty field');
    continue;
  }

  if (field === 'password') {
    console.log('Sensitive field found, stop logging.');
    break;
  }

  console.log('Checking:', field);
}

// → Checking: email
// → Skipping empty field
// → Sensitive field found, stop logging.
Nested loops for a grid ▶ Try it
for (let row = 1; row <= 3; row++) {
  let line = '';

  for (let col = 1; col <= 4; col++) {
    line += `[${row},${col}] `;
  }

  console.log(line.trim());
}

⚙️

Functions

Reusable blocks of code. The building block of every JS program.

Functions are how you package logic into reusable, testable steps. In real projects you use them to format labels, calculate totals, validate forms, transform API data, and keep UI code readable. Learn the basics first, then pay attention to default values, rest parameters, and callbacks because those patterns show up constantly in production JavaScript.
Format user labels ▶ Try it
function formatUserLabel(name, role) {
  return `${name.trim()} · ${role.toUpperCase()}`;
}

const formatStatus = status => `Status: ${status.toLowerCase()}`;

console.log(formatUserLabel(' Adrian ', 'mentor'));
console.log(formatStatus('ACTIVE'));
// → Adrian · MENTOR
// → Status: active
Default params & rest ▶ Try it
function calculateInvoiceTotal(taxRate = 0.19, ...items) {
  const subtotal = items.reduce((sum, item) => sum + item.price * item.qty, 0);
  return +(subtotal * (1 + taxRate)).toFixed(2);
}

const total = calculateInvoiceTotal(
  0.19,
  { price: 120, qty: 1 },
  { price: 35, qty: 2 }
);

console.log('Invoice total:', total); // → 226.1
Validation callback ▶ Try it
function validateField(label, value, rule) {
  if (!rule(value)) {
    return `${label} is invalid`;
  }

  return `${label} looks good`;
}

const emailRule = text => text.includes('@') && text.includes('.');

console.log(validateField('Email', 'team@example.com', emailRule));
console.log(validateField('Email', 'wrong-value', emailRule));
Return structured data ▶ Try it
function buildOrderSummary(customer, items) {
  const total = items.reduce((sum, item) => sum + item.price, 0);

  return {
    customer,
    itemCount: items.length,
    total,
    message: `${customer} ordered ${items.length} items.`
  };
}

const summary = buildOrderSummary('Mara', [
  { name: 'Notebook', price: 24 },
  { name: 'Pen set', price: 11 }
]);

console.log(summary.message);
console.log(summary.total); // → 35
Composable helpers ▶ Try it
const trim = value => value.trim();
const lowercase = value => value.toLowerCase();
const toSlug = value => value.replaceAll(' ', '-');

function pipe(value, ...steps) {
  return steps.reduce((current, step) => step(current), value);
}

const slug = pipe('  Summer Sale Banner  ', trim, lowercase, toSlug);
console.log(slug); // → summer-sale-banner

📎

Closures

Functions that "remember" their surrounding scope.

A closure happens when a function keeps access to variables from the place where it was created, even after that outer code finished running. In real apps, closures power private state, reusable builders, memoized helpers, debounced actions, and configuration-based callbacks.
Private draft state ▶ Try it
function createDraftTracker() {
  let lastSavedAt = 'not saved yet';
  
  return function markSaved(time) {
    lastSavedAt = time;
    console.log(`Draft saved at ${lastSavedAt}`);
  };
}

const saveDraft = createDraftTracker();
saveDraft('10:45');
saveDraft('10:52');
Counter with closure ▶ Try it
function makeCounter() {
  let count = 0;
  
  return {
    increment: () => ++count,
    decrement: () => --count,
    get: () => count
  };
}

const counter = makeCounter();
console.log(counter.increment());  // → 1
console.log(counter.increment());  // → 2
console.log(counter.decrement());  // → 1
Factory for request labels ▶ Try it
function createRequestLabel(prefix) {
  return id => `${prefix}-${id}`;
}

const draftLabel = createRequestLabel('draft');
const publishLabel = createRequestLabel('publish');

console.log(draftLabel(101)); // → draft-101
console.log(publishLabel(101)); // → publish-101
Memoized formatter ▶ Try it
function createPriceFormatter() {
  const cache = {};

  return amount => {
    if (cache[amount]) return cache[amount];
    cache[amount] = `$${amount.toFixed(2)}`;
    return cache[amount];
  };
}

const formatPrice = createPriceFormatter();
console.log(formatPrice(49));
console.log(formatPrice(49)); // same cached result

📋

Arrays

Ordered lists of values. Master map, filter and reduce.

Arrays store ordered values like product lists, notifications, comments, or API results. The most common array methods help you transform data (map), filter what you need (filter), combine values (reduce), search quickly (find, some, every), and organize items with helpers like sort, slice, includes, and at.
map & filter▶ Try it
const nums = [1, 2, 3, 4, 5];

const doubled = nums.map(n => n * 2);
console.log(doubled); // → [2,4,6,8,10]

const evens = nums.filter(n => n % 2 === 0);
console.log(evens);   // → [2, 4]
const prices = [9.99, 4.50, 14.99, 2.00];

const total = prices.reduce(
  (sum, price) => sum + price, 0
);

console.log(total.toFixed(2)); // → 31.48
find, some & every▶ Try it
const users = [
  { name: 'Ana',  age: 22 },
  { name: 'Mihai', age: 17 },
];

const adult = users.find(u => u.age >= 18);
console.log(adult.name);  // → Ana

const hasMinor = users.some(u => u.age < 18);
console.log(hasMinor);     // → true
push, slice & sort▶ Try it
const tasks = ['Write brief', 'Review copy'];

tasks.push('Publish update');
console.log(tasks); // → 3 tasks

const today = tasks.slice(0, 2);
console.log(today); // → first 2 tasks

const prices = [19, 5, 12];
const sorted = [...prices].sort((a, b) => a - b);
console.log(sorted); // → [5, 12, 19]
Real cart summary▶ Try it
const cart = [
  { name: 'Keyboard', price: 99, qty: 1 },
  { name: 'Mouse', price: 49, qty: 2 },
  { name: 'Cable', price: 12, qty: 3 },
];

const labels = cart.map(item => `${item.name} x${item.qty}`);
const total = cart.reduce((sum, item) => sum + item.price * item.qty, 0);

console.log(labels);
console.log(`Total: $${total}`);
includes, at & destructuring▶ Try it
const statuses = ['draft', 'review', 'published'];

console.log(statuses.includes('review')); // → true
console.log(statuses.at(-1)); // → published

const [first, second] = statuses;
console.log(first, second); // → draft review

const [, , last] = statuses;
console.log(last); // → published
flatMap real tags▶ Try it
const posts = [
  { title: 'Design system', tags: ['css', 'ui'] },
  { title: 'Launch checklist', tags: ['ops', 'ui'] },
];

const allTags = posts.flatMap(post => post.tags);
const uniqueTags = [...new Set(allTags)].sort();

console.log(allTags); // → ['css', 'ui', 'ops', 'ui']
console.log(uniqueTags); // → ['css', 'ops', 'ui']

📦

Objects

Key-value pairs for structured data. The foundation of JS.

Objects group related data into named properties. They are ideal for users, products, settings, API responses, and form state. Modern JavaScript gives you destructuring, spread, Object methods, method shorthand, and optional chaining so you can read and update structured data safely and clearly.
Destructuring & Spread▶ Try it
const user = { name: 'Ana', age: 25, city: 'Cluj' };

const { name, age } = user;
console.log(name, age); // → Ana 25

const updated = { ...user, age: 26 };
console.log(updated.age); // → 26
Object methods▶ Try it
const scores = { Ana: 90, Mihai: 75, Ion: 88 };

console.log(Object.keys(scores));
// → ['Ana', 'Mihai', 'Ion']

console.log(Object.values(scores));
// → [90, 75, 88]
Optional Chaining▶ Try it
const config = {
  db: { host: 'localhost', port: 5432 }
};

console.log(config?.db?.host);   // → localhost
console.log(config?.cache?.host); // → undefined

const port = config?.cache?.port ?? 6379;
console.log(port); // → 6379
Nested object updates▶ Try it
const profile = {
  name: 'Elena',
  contact: { email: 'elena@studio.dev', city: 'Bucharest' },
  plan: 'starter',
};

const upgraded = {
  ...profile,
  plan: 'pro',
  contact: { ...profile.contact, city: 'Cluj-Napoca' },
};

console.log(upgraded.plan); // → pro
console.log(upgraded.contact.city); // → Cluj-Napoca
entries & fromEntries▶ Try it
const settings = {
  theme: 'dark',
  notifications: true,
  language: 'en',
};

const labels = Object.entries(settings).map(
  ([key, value]) => `${key}: ${value}`
);

const upperKeys = Object.fromEntries(
  Object.entries(settings).map(([key, value]) => [key.toUpperCase(), value])
);

console.log(labels);
console.log(upperKeys);
Method shorthand & this▶ Try it
const account = {
  owner: 'Mara',
  balance: 1200,
  deposit(amount) {
    this.balance += amount;
    return this.balance;
  },
  summary() {
    return `${this.owner}: $${this.balance}`;
  }
};

account.deposit(300);
console.log(account.summary()); // → Mara: $1500
Defaults with Object.assign▶ Try it
const defaultOptions = {
  pageSize: 20,
  sortBy: 'createdAt',
  showArchived: false,
};

const userOptions = {
  pageSize: 50,
  showArchived: true,
};

const finalOptions = Object.assign({}, defaultOptions, userOptions);

console.log(finalOptions.pageSize); // → 50
console.log(finalOptions.sortBy); // → createdAt

🏗️

Classes

Object-oriented programming with constructors and methods.

Classes help you model repeated structures with shared behavior. They are useful when you have many similar objects like users, notifications, invoices, UI widgets, or API clients. The key ideas are constructors, instance methods, inheritance, and controlled access through getters and setters.
Workspace class ▶ Try it
class Workspace {
  constructor(name, owner) {
    this.name = name;
    this.owner = owner;
    this.members = 1;
  }
  
  addMember() {
    this.members += 1;
  }

  summary() {
    return `${this.name} by ${this.owner} · ${this.members} members`;
  }
}

const workspace = new Workspace('Launch Board', 'Adrian');
workspace.addMember();
console.log(workspace.summary());
Inheritance for notifications ▶ Try it
class Notification {
  constructor(message) {
    this.message = message;
  }
  
  show() {
    console.log(`Notice: ${this.message}`);
  }
}

class SuccessNotification extends Notification {
  show() {
    console.log(`Success: ${this.message}`);
  }
}

const notice = new SuccessNotification('Project published');
notice.show(); // → Success: Project published
Getters & setters for progress ▶ Try it
class ProjectProgress {
  constructor() {
    this._value = 0;
  }
  
  get label() {
    return `${this._value}% complete`;
  }
  
  set value(nextValue) {
    this._value = Math.max(0, Math.min(100, nextValue));
  }
}

const progress = new ProjectProgress();
progress.value = 72;
console.log(progress.label); // → 72% complete
progress.value = 140;
console.log(progress.label); // → 100% complete
Static helper method ▶ Try it
class ColorTheme {
  static fromMode(mode) {
    return mode === 'dark' ? '#020617' : '#f8fafc';
  }
}

console.log(ColorTheme.fromMode('dark'));
console.log(ColorTheme.fromMode('light'));

🌐

DOM

Select, modify and react to elements on the page.

The Document Object Model is the page structure your script can read and update in real time. In practice you use it to populate cards, validate forms, toggle menus, render lists, and react to clicks without reloading the page. These examples work best in the Preview tab because you can see both the markup and the behavior together.
Update product card▶ Try it
const title = document.querySelector('[data-product-title]');
const price = document.querySelector('[data-product-price]');
const badge = document.querySelector('[data-product-badge]');

title.textContent = 'Wireless Keyboard';
price.textContent = '$89';
badge.textContent = 'In stock';
badge.classList.add('is-live');
Form submit & validation▶ Try it
const form = document.querySelector('form');
const message = document.querySelector('[data-message]');

form.addEventListener('submit', event => {
  event.preventDefault();
  const email = form.email.value.trim();

  message.textContent = email.includes('@')
    ? `Invite sent to ${email}`
    : 'Please enter a valid email address.';
});
Render a task list▶ Try it
const tasks = [
  { title: 'Write landing copy', done: true },
  { title: 'Review CTA button', done: false },
  { title: 'Publish update', done: false }
];

const list = document.querySelector('[data-task-list]');

list.innerHTML = tasks.map(task => `
  
  • ${task.title}
  • `
    ).join('');
    Event delegation menu▶ Try it
    const menu = document.querySelector('[data-menu]');
    const status = document.querySelector('[data-status]');
    
    menu.addEventListener('click', event => {
      const button = event.target.closest('button[data-view]');
      if (!button) return;
    
      status.textContent = `Active view: ${button.dataset.view}`;
    });
    Toggle theme panel▶ Try it
    const toggle = document.querySelector('[data-toggle-theme]');
    const panel = document.querySelector('[data-panel]');
    
    toggle.addEventListener('click', () => {
      panel.classList.toggle('dark');
      toggle.textContent = panel.classList.contains('dark')
        ? 'Switch to light'
        : 'Switch to dark';
    });
    Open and close modal▶ Try it
    const openButton = document.querySelector('[data-open-modal]');
    const closeButton = document.querySelector('[data-close-modal]');
    const modal = document.querySelector('[data-modal]');
    
    openButton.addEventListener('click', () => {
      modal.hidden = false;
    });
    
    closeButton.addEventListener('click', () => {
      modal.hidden = true;
    });
    Tabs interface▶ Try it
    const tabs = document.querySelectorAll('[data-tab]');
    const panels = document.querySelectorAll('[data-panel-name]');
    
    tabs.forEach(tab => {
      tab.addEventListener('click', () => {
        panels.forEach(panel => {
          panel.hidden = panel.dataset.panelName !== tab.dataset.tab;
        });
      });
    });
    Search and filter list▶ Try it
    const search = document.querySelector('[data-search]');
    const items = document.querySelectorAll('[data-item]');
    
    search.addEventListener('input', () => {
      const term = search.value.trim().toLowerCase();
    
      items.forEach(item => {
        item.hidden = !item.textContent.toLowerCase().includes(term);
      });
    });
    Mini dashboard stats▶ Try it
    const stats = {
      users: 1240,
      projects: 86,
      uptime: '99.98%'
    };
    
    document.querySelector('[data-users]').textContent = stats.users;
    document.querySelector('[data-projects]').textContent = stats.projects;
    document.querySelector('[data-uptime]').textContent = stats.uptime;

    Timers

    Schedule code with setTimeout and setInterval.

    Timers let you delay work, repeat work, and cancel work. They are useful for auto-save, dismissing alerts, retrying a request, countdowns, onboarding hints, and temporary UI states. The important part is understanding when code runs later and how to stop it when the interface changes.
    Dismiss a banner later ▶ Try it
    console.log('Banner shown');
    
    setTimeout(() => {
      console.log('Banner hidden after 2 seconds');
    }, 2000);
    Auto-save heartbeat ▶ Try it
    let saveCount = 0;
    
    const intervalId = setInterval(() => {
      saveCount++;
      console.log(`Auto-save ${saveCount}`);
      
      if (saveCount >= 3) {
        clearInterval(intervalId);
        console.log('Stop auto-save checks');
      }
    }, 500);
            
    Cancel scheduled action ▶ Try it
    const redirectId = setTimeout(() => {
      console.log('Redirect to billing page');
    }, 3000);
    
    console.log('Redirect scheduled');
    clearTimeout(redirectId);
    console.log('Redirect canceled');
    Countdown timer ▶ Try it
    let secondsLeft = 3;
    
    const countdownId = setInterval(() => {
      console.log(`Refreshing in ${secondsLeft}...`);
      secondsLeft--;
    
      if (secondsLeft < 0) {
        clearInterval(countdownId);
        console.log('Refresh now');
      }
    }, 1000);

    Async / Await

    Write asynchronous code that reads like synchronous code.

    JavaScript is non-blocking, so work like timers, network requests, and file uploads finishes later while the page stays responsive. Promises describe a future result, and async/await lets you write that flow clearly. In real apps you also need parallel requests, loading states, and error handling.
    Simulate save request▶ Try it
    function saveDraft(title) {
      return new Promise(resolve => {
        setTimeout(() => {
          resolve({ id: 101, title, savedAt: '10:30' });
        }, 600);
      });
    }
    
    console.log('Saving draft...');
    saveDraft('Homepage hero copy').then(result => {
      console.log('Saved:', result.title, result.savedAt);
    });
    Sequential async steps▶ Try it
    const wait = ms => new Promise(resolve => setTimeout(resolve, ms));
    
    async function publishArticle() {
      console.log('1. Saving content');
      await wait(300);
      console.log('2. Generating preview');
      await wait(300);
      console.log('3. Article published');
    }
    
    publishArticle();
    Parallel loading with Promise.all▶ Try it
    const waitFor = (label, ms) =>
      new Promise(resolve => setTimeout(() => resolve(label), ms));
    
    async function loadDashboard() {
      const [stats, messages, alerts] = await Promise.all([
        waitFor('stats', 400),
        waitFor('messages', 250),
        waitFor('alerts', 500)
      ]);
    
      console.log('Loaded:', stats, messages, alerts);
    }
    
    loadDashboard();
    try, catch & finally▶ Try it
    async function loadProfile(shouldFail) {
      console.log('Loading profile...');
    
      try {
        if (shouldFail) {
          throw new Error('Network temporarily unavailable');
        }
    
        return { name: 'Daria', role: 'Editor' };
      } catch (err) {
        console.log(`Error: ${err.message}`);
        return null;
      } finally {
        console.log('Hide loading spinner');
      }
    }
    
    loadProfile(false).then(profile => console.log(profile));

    🔌

    Working with APIs

    Fetch real data from the internet using fetch().

    APIs let your app talk to servers for products, users, analytics, payments, and more. In the browser, fetch() sends HTTP requests and returns a Promise. The important real-world skills are reading JSON, checking response.ok, sending request bodies, and handling multiple requests without turning your code into callback soup.
    Load one post▶ Try it
    async function loadPost() {
      const response = await fetch('https://jsonplaceholder.typicode.com/posts/1');
      const post = await response.json();
    
      console.log('Title:', post.title);
      console.log('Preview:', post.body.slice(0, 40) + '...');
    }
    
    loadPost();
    Load multiple posts▶ Try it
    async function loadFeaturedPosts() {
      const ids = [1, 2, 3];
      const posts = await Promise.all(
        ids.map(id =>
          fetch(`https://jsonplaceholder.typicode.com/posts/${id}`).then(res => res.json())
        )
      );
    
      posts.forEach(post => console.log(post.id, post.title));
    }
    
    loadFeaturedPosts();
    Send feedback with POST▶ Try it
    async function sendFeedback() {
      const response = await fetch('https://jsonplaceholder.typicode.com/posts', {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify({
          name: 'Adrian',
          message: 'Editor feels fast and simple.'
        })
      });
    
      const result = await response.json();
      console.log('Saved with id:', result.id);
    }
    
    sendFeedback();
    Check response.ok▶ Try it
    async function loadSafe() {
      try {
        const response = await fetch('https://jsonplaceholder.typicode.com/posts/9999');
    
        if (!response.ok) {
          throw new Error(`Request failed with ${response.status}`);
        }
    
        const data = await response.json();
        console.log(data);
      } catch (error) {
        console.log('Handled error:', error.message);
      }
    }
    
    loadSafe();

    Ready to practice?

    Open the editor and try practical examples for logic, data, UI interactions, timing, async flows, and API work. No setup, no install.

    All examples run instantly · No account needed · Free forever