三十的博客

JavaScript 异步编程精髓(系列03)

本文内容基于 AI 生成结果整理,可能包含不准确信息,仅供参考使用。
发布时间
阅读量 加载中...

一、Promise 基础概念

1. 什么是 Promise?

Promise 是一个表示异步操作最终完成或失败的对象。想象你点了一份外卖:

javascript
// 创建一个简单的 Promise
const myPromise = new Promise((resolve, reject) => {
  // 这里是异步操作,比如 API 请求
  setTimeout(() => {
    const success = Math.random() > 0.5; // 模拟50%成功率
    if (success) {
      resolve("操作成功!"); // 外卖送达
    } else {
      reject("操作失败!"); // 外卖送丢了
    }
  }, 1000);
});

2. 基本使用方法

javascript
myPromise
  .then((result) => {
    console.log(result); // 成功时执行
  })
  .catch((error) => {
    console.error(error); // 失败时执行
  })
  .finally(() => {
    console.log("操作完成"); // 无论成功失败都会执行
  });

二、创建 Promise 的三种方式

1. 使用构造函数

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

delay(1000).then(() => console.log("1秒后执行"));

2. 快捷方法

javascript
// 立即成功的 Promise
Promise.resolve("直接成功").then(console.log);

// 立即失败的 Promise
Promise.reject("直接失败").catch(console.error);

3. 转换回调函数

javascript
// 将回调风格的函数转换为 Promise
const fs = require("fs");
const util = require("util");

// 传统回调方式
fs.readFile("file.txt", "utf8", (err, data) => {
  if (err) throw err;
  console.log(data);
});

// Promise 方式
const readFile = util.promisify(fs.readFile);
readFile("file.txt", "utf8").then(console.log).catch(console.error);

三、多 Promise 协作

1. Promise.all - 等待所有完成

javascript
const p1 = Promise.resolve(1);
const p2 = Promise.resolve(2);
const p3 = new Promise((resolve) => setTimeout(resolve, 1000, 3));

Promise.all([p1, p2, p3])
  .then((values) => {
    console.log(values); // [1, 2, 3] (约1秒后)
  })
  .catch((error) => {
    console.error(error); // 如果任何一个失败
  });

2. Promise.race - 竞速模式

javascript
const fast = new Promise((resolve) => setTimeout(resolve, 500, "快的赢了"));
const slow = new Promise((resolve) => setTimeout(resolve, 1000, "慢的赢了"));

Promise.race([fast, slow]).then((winner) => {
  console.log(winner); // "快的赢了" (500ms后)
});

3. Promise.allSettled - 获取所有结果

javascript
const success = Promise.resolve("成功");
const failure = Promise.reject("失败");

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

四、async/await 语法糖

1. 基础用法

javascript
async function fetchUser() {
  try {
    const response = await fetch("https://api.example.com/user");
    const data = await response.json();
    console.log(data);
    return data;
  } catch (error) {
    console.error("获取用户失败:", error);
    throw error; // 可以继续向上抛出错误
  }
}

// 调用 async 函数
fetchUser().then((user) => console.log("最终用户:", user));

2. 并行优化

javascript
// 错误写法 - 顺序执行
async function slowFetch() {
  const user = await fetch("/user"); // 等待完成
  const orders = await fetch("/orders"); // 再等待
  return { user, orders };
}

// 正确写法 - 并行执行
async function fastFetch() {
  const userPromise = fetch("/user");
  const ordersPromise = fetch("/orders");
  return {
    user: await userPromise,
    orders: await ordersPromise,
  };
}

五、高级应用场景

1. 请求取消

javascript
function createCancellableRequest() {
  let controller = new AbortController();

  const request = fetch("/api/data", {
    signal: controller.signal,
  }).catch((err) => {
    if (err.name === "AbortError") {
      console.log("请求被取消");
    } else {
      throw err;
    }
  });

  return {
    promise: request,
    cancel: () => controller.abort(),
  };
}

const { promise, cancel } = createCancellableRequest();

// 5秒后取消请求
setTimeout(cancel, 5000);

2. 自动重试机制

javascript
async function retry(fn, maxRetries = 3, delay = 1000) {
  try {
    return await fn();
  } catch (err) {
    if (maxRetries <= 0) throw err;
    console.log(`重试中... 剩余 ${maxRetries} 次`);
    await new Promise((resolve) => setTimeout(resolve, delay));
    return retry(fn, maxRetries - 1, delay * 2); // 指数退避
  }
}

// 使用示例
retry(() => fetch("/unstable-api"))
  .then(console.log)
  .catch(console.error);

3. 异步队列控制

javascript
class AsyncQueue {
  constructor(concurrency = 1) {
    this.queue = [];
    this.activeCount = 0;
    this.concurrency = concurrency;
  }

  enqueue(task) {
    return new Promise((resolve, reject) => {
      const runTask = () => {
        this.activeCount++;
        task()
          .then(resolve, reject)
          .finally(() => {
            this.activeCount--;
            if (this.queue.length) this.queue.shift()();
          });
      };

      if (this.activeCount < this.concurrency) {
        runTask();
      } else {
        this.queue.push(runTask);
      }
    });
  }
}

// 使用示例
const queue = new AsyncQueue(2); // 并发数2

for (let i = 0; i < 10; i++) {
  queue
    .enqueue(() => fetch(`/api/item/${i}`))
    .then((data) => console.log(`任务 ${i} 完成`));
}

六、错误处理最佳实践

1. 全局错误捕获

javascript
// 浏览器环境
window.addEventListener("unhandledrejection", (event) => {
  console.error("未处理的Promise拒绝:", event.reason);
  event.preventDefault(); // 阻止默认错误打印
});

// Node.js环境
process.on("unhandledRejection", (reason, promise) => {
  console.error("未处理的拒绝:", reason);
});

2. 错误边界处理

javascript
async function safeFetch(url) {
  try {
    const response = await fetch(url);
    if (!response.ok) {
      throw new Error(`HTTP错误! 状态码: ${response.status}`);
    }
    return await response.json();
  } catch (error) {
    console.error(`请求 ${url} 失败:`, error);
    throw error; // 可以选择继续抛出或返回默认值
  }
}
#Javascript #Promise