JavaScript 异步编程精髓(系列03)
本文内容基于 AI 生成结果整理,可能包含不准确信息,仅供参考使用。
一、Promise 基础概念
1. 什么是 Promise?
Promise 是一个表示异步操作最终完成或失败的对象。想象你点了一份外卖:
- Pending(等待中):外卖已下单,但还没送到
- Fulfilled(已完成):外卖成功送达
- Rejected(已拒绝):外卖送餐失败
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; // 可以选择继续抛出或返回默认值
}
}