首页
/ JavaScript 异步编程实战指南:从回调到 Promise 与生成器

JavaScript 异步编程实战指南:从回调到 Promise 与生成器

2025-07-08 07:38:46作者:郦嵘贵Just

前言

在现代 JavaScript 开发中,异步编程是每个开发者必须掌握的核心技能。本文将深入探讨 JavaScript 异步编程的各种技术实现,帮助开发者从基础到进阶全面理解异步编程范式。

1. 顺序执行异步代码块

最基本的异步控制模式是确保多个异步操作按顺序执行。我们可以通过回调嵌套实现:

function asyncFunc() {
  console.log("开始 asyncFunc1");
  setTimeout(() => {
    console.log("完成 asyncFunc1");
    console.log("开始 asyncFunc2");
    setTimeout(() => {
      console.log("完成 asyncFunc2");
      console.log("开始 asyncFunc3");
      setTimeout(() => {
        console.log("完成 asyncFunc3");
      }, 1000);
    }, 2000);
  }, 3000);
}

这种模式虽然直观,但会导致"回调地狱",代码难以维护。接下来我们将看到更好的解决方案。

2. XMLHttpRequest 基础

传统 AJAX 请求使用 XMLHttpRequest 对象:

const xhr = new XMLHttpRequest();
xhr.open("GET", url);
xhr.onload = function() {
  console.log(this.response);
};
xhr.onerror = function() {
  console.log(this.statusText);
};
xhr.send();

XHR 提供了基本的异步请求能力,但现代开发中已被更先进的 API 取代。

3. Promise 基础

Promise 是现代 JavaScript 异步编程的核心:

function asyncResolveFunc() {
  return new Promise((resolve) => resolve("成功"));
}

function asyncRejectFunc() {
  return new Promise((_, reject) => reject("失败"));
}

const promiseSuccess = asyncResolveFunc();
const promiseFailure = asyncRejectFunc();

promiseSuccess.then(
  (data) => console.log(data),
  (err) => console.log(err)
);

promiseFailure.then(
  (data) => console.log(data),
  (err) => console.log(err))
);

Promise 有三种状态:pending、fulfilled 和 rejected,状态一旦确定就不可更改。

4. 将 setTimeout 包装为 Promise

我们可以将回调式 API 转换为 Promise:

function setTimeoutPromise(delay) {
  return new Promise((resolve) => setTimeout(resolve, delay));
}

console.log("任务开始");
setTimeoutPromise(3000).then(() => console.log("任务完成"));

这种模式极大提高了代码可读性。

5. 基于 Promise 的 XHR 封装

将 XHR 封装为 Promise 使代码更清晰:

function fetchData(url) {
  return new Promise((resolve, reject) => {
    const xhr = new XMLHttpRequest();
    xhr.open("GET", url);
    xhr.onload = function() {
      this.status === 200 ? resolve(this) : reject(this);
    };
    xhr.onerror = () => reject(this);
    xhr.send();
  });
}

6. 使用 Fetch API

现代浏览器提供了更简洁的 Fetch API:

fetch("https://reqbin.com/echo/get/json", {
  method: "GET",
  headers: { "Content-Type": "application/json" }
})
  .then(response => response.json())
  .then(data => console.log(data))
  .catch(err => console.log(err));

7. 取消 Fetch 请求

使用 AbortController 可以取消请求:

const controller = new AbortController();
fetch(url, { signal: controller.signal })
  .then(response => console.log(response))
  .catch(err => console.warn(err));

controller.abort();

8. async/await 语法

async/await 让异步代码看起来像同步代码:

async function asyncAwaitFunc() {
  try {
    console.log("正常执行");
    await promiseReturningFunc();
    console.log("Promise 解决后继续执行");
  } catch (err) {
    console.log("错误: " + err);
  }
}

9. Promise.all 批量处理

并行执行多个 Promise,全部成功或任一失败:

const promises = [async1(), async2(), async3()];

// 使用 then/catch
Promise.all(promises)
  .then(results => results.forEach(data => console.log(data)))
  .catch(err => console.log(err));

// 使用 async/await
(async () => {
  try {
    const results = await Promise.all(promises);
    results.forEach(data => console.log(data));
  } catch (err) {
    console.log(err);
  }
})();

10. Promise.allSettled 处理所有结果

无论成功失败都等待所有 Promise 完成:

Promise.allSettled(promises).then(results => {
  results.forEach(result => {
    if (result.status === "fulfilled") {
      console.log("成功:", result.value);
    } else {
      console.log("失败:", result.reason);
    }
  });
});

11. Promise.race 竞速

第一个完成的 Promise 决定结果:

Promise.race([
  new Promise(resolve => setTimeout(() => resolve("A"), 2000)),
  new Promise((_, reject) => setTimeout(() => reject("B"), 1000))
])
.then(console.log)
.catch(console.log); // 输出 B

12. 生成器函数基础

生成器可以暂停和恢复函数执行:

function* generatorFunc() {
  const a = yield;
  const b = yield;
  return a + b;
}

const it = generatorFunc();
it.next();       // { value: undefined, done: false }
it.next(3);      // { value: undefined, done: false }
const sum = it.next(5); // { value: 8, done: true }
console.log(sum.value); // 8

13. 生成器组合

生成器可以嵌套使用:

function* gen1() {
  yield 1;
  yield* gen2();
  yield 4;
}

function* gen2() {
  yield 2;
  yield 3;
}

for (const value of gen1()) {
  console.log(value); // 输出 1, 2, 3, 4
}

14. 实现简易 Promise

理解 Promise 的工作原理:

function MyPromise(resolver) {
  let successCallbacks = [];
  let failureCallbacks = [];
  let state = "pending";
  let value;
  
  function resolve(val) {
    if (state === "pending") {
      state = "fulfilled";
      value = val;
      successCallbacks.forEach(cb => cb(value));
    }
  }
  
  function reject(reason) {
    if (state === "pending") {
      state = "rejected";
      value = reason;
      failureCallbacks.forEach(cb => cb(value));
    }
  }
  
  setTimeout(() => resolver(resolve, reject), 0);
  
  return {
    then: function(onFulfilled, onRejected) {
      if (state === "fulfilled") {
        onFulfilled(value);
      } else if (state === "rejected") {
        onRejected(value);
      } else {
        successCallbacks.push(onFulfilled);
        failureCallbacks.push(onRejected);
      }
    }
  };
}

15. 实现 Promise.all

function PromiseAll(promises) {
  return new Promise((resolve, reject) => {
    const results = [];
    let completed = 0;
    
    promises.forEach((promise, index) => {
      promise.then(
        value => {
          results[index] = value;
          if (++completed === promises.length) {
            resolve(results);
          }
        },
        reason => reject(reason)
      );
    });
  });
}

16. 生成器与 Promise 结合

实现类似 async/await 的效果:

function asyncTask(data, delay) {
  return new Promise(resolve => setTimeout(() => resolve(data), delay));
}

function* main() {
  const a = yield asyncTask(2, 1000);
  const b = yield asyncTask(3, 1500);
  console.log(a + b); // 5
}

function runGenerator(gen) {
  const it = gen();
  
  function handle(result) {
    if (result.done) return;
    result.value.then(
      data => handle(it.next(data)),
      err => it.throw(err)
    );
  }
  
  handle(it.next());
}

runGenerator(main);

总结

本文全面介绍了 JavaScript 异步编程的各种技术,从基础的回调函数到 Promise、async/await 和生成器。掌握这些技术对于现代 JavaScript 开发至关重要,能够帮助开发者编写更清晰、更易维护的异步代码。