404 Not Found

404 Not Found


nginx

JavaScript Async Programming

JavaScript is single-threaded — it can only do one thing at a time. But if a network request takes 3 seconds, should the page freeze for 3 seconds? Of course not. Async programming solves this: instead of waiting around, you take a number and come back when it's ready. Like a restaurant queue system — you don't stand at the kitchen door, you sit down and wait for your number to be called.


Sync vs Async

Synchronous: Code executes line by line — the next line waits for the current one to finish. Like standing in line — you wait until the person ahead of you is done.

Asynchronous: Certain operations (network requests, timers, file I/O) start without blocking subsequent code. When they finish, you're notified via a callback or Promise.

HTML
<script>
console.log('1');
setTimeout(function() {
  console.log('2');
}, 1000);
console.log('3');
</script>

The output order is 1 → 3 → 2. setTimeout is asynchronous — its callback runs after 1 second without blocking console.log('3').


Callbacks

Callbacks are the most basic async pattern — pass a function as an argument, and it gets called when the async operation completes.

Example: Basic Callback Usage

HTML
<div id="output" style="padding: 10px; border: 1px solid #ccc;"></div>

<script>
const output = document.getElementById('output');

function fetchData(callback) {
  output.textContent = 'Loading...';
  setTimeout(function() {
    callback('Data loaded!');
  }, 2000);
}

fetchData(function(data) {
  output.textContent = data;
});
</script>
▶ Try it Yourself

Callback Hell

When async operations depend on each other in sequence, callbacks nest deeper and deeper — this is the infamous "Callback Hell."

HTML
<script>
getUser(function(user) {
  getOrders(user.id, function(orders) {
    getOrderDetail(orders[0].id, function(detail) {
      getItems(detail.itemId, function(items) {
        console.log('4 levels deep — add more complexity and it becomes unreadable');
      });
    });
  });
});
</script>
💡 Callback hell looks like a "horizontal pyramid" — hard to read and maintain. Promises were invented to solve this.


Promise

A Promise is an object that represents the eventual result of an async operation. It has three states:

State Description
pending In progress, no result yet
fulfilled Completed successfully, result available
rejected Failed, error available

Once a Promise transitions from pending to fulfilled or rejected, it never changes back — like an arrow once shot, it can't be recalled.

Creating a Promise

HTML
<script>
const promise = new Promise(function(resolve, reject) {
  // async operation
  // call resolve(result) on success
  // call reject(error) on failure
});
</script>

then / catch / finally

HTML
<script>
promise
  .then(function(result) { console.log(result); })   // runs on success
  .catch(function(error) { console.error(error); })    // runs on failure
  .finally(function() { console.log('Done'); });       // runs regardless
</script>

Example: Simulating a Request with Promise

HTML
<button id="loadBtn">Load Data</button>
<div id="output" style="padding: 10px; border: 1px solid #ccc; margin-top: 10px;"></div>

<script>
const btn = document.getElementById('loadBtn');
const output = document.getElementById('output');

function fetchUser() {
  return new Promise(function(resolve, reject) {
    output.textContent = 'Loading...';
    setTimeout(function() {
      const success = true;
      if (success) {
        resolve({ name: 'Alice', age: 25 });
      } else {
        reject(new Error('Load failed'));
      }
    }, 1500);
  });
}

btn.addEventListener('click', function() {
  fetchUser()
    .then(function(user) {
      output.textContent = 'Name: ' + user.name + ', Age: ' + user.age;
    })
    .catch(function(err) {
      output.textContent = 'Error: ' + err.message;
    })
    .finally(function() {
      console.log('Request completed (success or failure)');
    });
});
</script>
▶ Try it Yourself

Chaining

then itself returns a Promise, so you can chain calls — callback hell becomes a straight line.

HTML
<script>
fetchUser()
  .then(function(user) { return fetchOrders(user.id); })
  .then(function(orders) { return fetchDetail(orders[0].id); })
  .then(function(detail) { console.log(detail); })
  .catch(function(err) { console.error(err); });
</script>
💡 A single catch at the end of the chain catches errors from any step — you don't need a catch after every then.


async / await

async/await is syntactic sugar over Promises — it makes async code look synchronous, greatly improving readability.

Example: Rewriting the Above Request with async/await

HTML
<button id="loadBtn">Load Data</button>
<div id="output" style="padding: 10px; border: 1px solid #ccc; margin-top: 10px;"></div>

<script>
const btn = document.getElementById('loadBtn');
const output = document.getElementById('output');

function fetchUser() {
  return new Promise(function(resolve) {
    setTimeout(function() {
      resolve({ name: 'Bob', age: 30 });
    }, 1500);
  });
}

btn.addEventListener('click', async function() {
  try {
    output.textContent = 'Loading...';
    const user = await fetchUser();
    output.textContent = 'Name: ' + user.name + ', Age: ' + user.age;
  } catch (err) {
    output.textContent = 'Error: ' + err.message;
  } finally {
    console.log('Request completed');
  }
});
</script>
▶ Try it Yourself
💡 Compared to callbacks, async/await turns "take a number and wait" into "stay in line without leaving" — the code flow is straight and readability skyrockets.

Error Handling

async/await uses try/catch for error handling — the same pattern used with synchronous code.

HTML
<script>
async function loadAll() {
  try {
    const user = await fetchUser();
    const orders = await fetchOrders(user.id);
    console.log(orders);
  } catch (err) {
    console.error('Something went wrong:', err);
  }
}
</script>

Promise.all and Promise.race

Method Description
Promise.all([p1, p2, p3]) Succeeds only if all succeed; fails as soon as one fails
Promise.race([p1, p2, p3]) Uses the result of whichever settles first (success or failure)

Example: Concurrent Requests with Promise.all

HTML
<button id="loadAll">Load Concurrently</button>
<div id="output" style="padding: 10px; border: 1px solid #ccc; margin-top: 10px;"></div>

<script>
const btn = document.getElementById('loadAll');
const output = document.getElementById('output');

function delay(ms, value) {
  return new Promise(function(resolve) {
    setTimeout(function() { resolve(value); }, ms);
  });
}

btn.addEventListener('click', async function() {
  output.textContent = 'Loading...';

  const results = await Promise.all([
    delay(1000, 'User Data'),
    delay(1500, 'Order Data'),
    delay(800, 'Config Data')
  ]);

  output.textContent = 'All loaded:\n' + results.join(' | ');
});

const raceBtn = document.createElement('button');
raceBtn.textContent = 'Race Load';
document.body.appendChild(raceBtn);

raceBtn.addEventListener('click', async function() {
  const fastest = await Promise.race([
    delay(1000, 'Source A (1s)'),
    delay(500, 'Source B (0.5s)'),
    delay(800, 'Source C (0.8s)')
  ]);
  output.textContent = 'Fastest: ' + fastest;
});
</script>
▶ Try it Yourself
💡 Promise.all is like a group dinner — everyone must arrive before you eat. Promise.race is like a race — whoever finishes first wins. Use all for independent requests to speed things up, and race when you have multiple mirror sources and want the fastest one.


📖 Summary

  1. Sync code blocks subsequent execution; async code doesn't — single-threaded can still "do multiple things"
  2. Callbacks are the most basic async pattern; deep nesting creates callback hell
  3. Promise has three states: pending → fulfilled or pending → rejected — state transitions are irreversible
  4. then/catch/finally consume Promises; chaining solves callback hell
  5. async/await is syntactic sugar over Promises; await can only be used inside async functions
  6. Promise.all waits for all to succeed; Promise.race takes the first to settle

❓ FAQ

Q: Can await be used in a regular function? A: No. await can only be used inside an async function. Using await in a regular function causes a syntax error. Top-level await is supported in modules, but browser support varies.

Q: What happens if one of the Promises in Promise.all fails? A: Promise.all immediately rejects with the first error. Other Promises still execute, but their results are ignored. If you need "all settled regardless of outcome," use Promise.allSettled.

Q: What does an async function return? A: An async function always returns a Promise. Even if you return 42, it actually returns Promise.resolve(42). So you can use .then() to consume the result.

📝 Exercises

  1. Basic: Use Promise + setTimeout to simulate a 2-second delay, then output "Done" on success.
  2. Intermediate: Rewrite the above with async/await and add try/catch error handling.
  3. Challenge: Use Promise.all to fire 3 simulated requests with different delays (1s/2s/3s) concurrently, then calculate and display the total elapsed time when all complete.
100%

🙏 帮我们做得更好

我们是刚上线的编程教程站,几个人的小团队,精力有限。页面虽经检查,难免还有疏漏——链接失效、排版错乱、内容有误、语言生硬……

如果您发现了,麻烦告诉我们,我们会在收到反馈后第一时间进行修复,再次感谢您的光临 🙏