Promise 详解速览
1. Promise 概述
Promise 是 JavaScript 中处理异步操作的一种方式,它代表了一个异步操作的最终完成(或失败)及其结果值。
核心概念:
- 状态管理:Promise 有三种状态 - pending(待定)、fulfilled(已成功)、rejected(已失败)
- 不可逆性:一旦状态改变,就不能再改变
- 链式调用:支持链式调用,避免回调地狱
- 错误处理:统一的错误处理机制
Promise 状态:
- Pending(待定):初始状态,既没有被兑现,也没有被拒绝
- Fulfilled(已成功):操作成功完成
- Rejected(已失败):操作失败
2. Promise 基础用法
2.1 创建 Promise
// 创建 Promise
const promise = new Promise((resolve, reject) => {
// 异步操作
setTimeout(() => {
const success = Math.random() > 0.5;
if (success) {
resolve('操作成功!');
} else {
reject(new Error('操作失败!'));
}
}, 1000);
});
// 使用 Promise
promise
.then(result => {
console.log('成功:', result);
})
.catch(error => {
console.error('失败:', error.message);
});
2.2 Promise 状态转换
// Promise 状态只能改变一次
const promise1 = new Promise((resolve, reject) => {
resolve('第一次 resolve');
reject('第二次 reject'); // 这个会被忽略
});
promise1.then(result => {
console.log(result); // 输出: 第一次 resolve
});
// 已完成的 Promise
const resolvedPromise = Promise.resolve('已解决的值');
const rejectedPromise = Promise.reject(new Error('已拒绝的原因'));
resolvedPromise.then(result => {
console.log(result); // 输出: 已解决的值
});
rejectedPromise.catch(error => {
console.error(error.message); // 输出: 已拒绝的原因
});
3. Promise 链式调用
3.1 基本链式调用
// 链式调用
new Promise((resolve, reject) => {
setTimeout(() => resolve(1), 1000);
})
.then(result => {
console.log(result); // 1
return result * 2;
})
.then(result => {
console.log(result); // 2
return result * 3;
})
.then(result => {
console.log(result); // 6
return Promise.resolve(result * 4);
})
.then(result => {
console.log(result); // 24
})
.catch(error => {
console.error('错误:', error);
});
3.2 链式调用中的错误处理
// 错误处理链
new Promise((resolve, reject) => {
setTimeout(() => resolve(1), 1000);
})
.then(result => {
console.log('第一步:', result); // 1
return result * 2;
})
.then(result => {
console.log('第二步:', result); // 2
throw new Error('在第二步发生错误');
return result * 3;
})
.then(result => {
// 这个不会执行
console.log('第三步:', result);
return result * 4;
})
.catch(error => {
// 捕获错误
console.error('捕获错误:', error.message); // 在第二步发生错误
// 返回默认值继续链式调用
return 0;
})
.then(result => {
// 错误处理后继续执行
console.log('最终结果:', result); // 0
});
4. Promise 静态方法
4.1 Promise.resolve()
// Promise.resolve() - 创建已解决的 Promise
const resolved1 = Promise.resolve('直接值');
const resolved2 = Promise.resolve(Promise.resolve('嵌套 Promise'));
const resolved3 = Promise.resolve({ name: 'John', age: 30 });
resolved1.then(value => console.log(value)); // 直接值
resolved2.then(value => console.log(value)); // 嵌套 Promise
resolved3.then(value => console.log(value)); // { name: 'John', age: 30 }
4.2 Promise.reject()
// Promise.reject() - 创建已拒绝的 Promise
const rejected = Promise.reject(new Error('拒绝原因'));
rejected.catch(error => {
console.error(error.message); // 拒绝原因
});
4.3 Promise.all()
// Promise.all() - 所有 Promise 都成功才成功
const promises = [
Promise.resolve(1),
Promise.resolve(2),
Promise.resolve(3)
];
Promise.all(promises)
.then(results => {
console.log(results); // [1, 2, 3]
})
.catch(error => {
console.error('其中一个失败:', error);
});
// 有一个失败的情况
const mixedPromises = [
Promise.resolve('成功1'),
Promise.reject(new Error('失败')),
Promise.resolve('成功2')
];
Promise.all(mixedPromises)
.then(results => {
// 不会执行
console.log(results);
})
.catch(error => {
console.error('失败:', error.message); // 失败
});
4.4 Promise.allSettled()
// Promise.allSettled() - 等待所有 Promise 完成(无论成功或失败)
const mixedPromises = [
Promise.resolve('成功1'),
Promise.reject(new Error('失败')),
Promise.resolve('成功2')
];
Promise.allSettled(mixedPromises)
.then(results => {
results.forEach((result, index) => {
if (result.status === 'fulfilled') {
console.log(`Promise ${index} 成功:`, result.value);
} else {
console.log(`Promise ${index} 失败:`, result.reason.message);
}
});
});
// 输出:
// Promise 0 成功: 成功1
// Promise 1 失败: 失败
// Promise 2 成功: 成功2
4.5 Promise.race()
// Promise.race() - 第一个完成的 Promise 决定结果
const promise1 = new Promise(resolve => setTimeout(() => resolve('第一个'), 100));
const promise2 = new Promise(resolve => setTimeout(() => resolve('第二个'), 200));
const promise3 = new Promise((resolve, reject) => setTimeout(() => reject(new Error('第三个失败')), 300));
Promise.race([promise1, promise2, promise3])
.then(result => {
console.log('最快的结果:', result); // 第一个
})
.catch(error => {
console.error('最快的错误:', error.message);
});
// 实际应用:超时控制
function timeout(promise, ms) {
const timeoutPromise = new Promise((resolve, reject) => {
setTimeout(() => reject(new Error('超时')), ms);
});
return Promise.race([promise, timeoutPromise]);
}
// 使用超时控制
timeout(fetch('/api/data'), 5000)
.then(response => response.json())
.then(data => console.log(data))
.catch(error => {
if (error.message === '超时') {
console.error('请求超时');
} else {
console.error('其他错误:', error);
}
});
4.6 Promise.any()
// Promise.any() - 第一个成功的 Promise 决定结果(ES2021)
const promises = [
Promise.reject(new Error('失败1')),
Promise.resolve('成功1'),
Promise.resolve('成功2')
];
Promise.any(promises)
.then(result => {
console.log('第一个成功:', result); // 成功1
})
.catch(error => {
console.error('所有都失败:', error);
});
// 所有都失败的情况
const allFailed = [
Promise.reject(new Error('失败1')),
Promise.reject(new Error('失败2'))
];
Promise.any(allFailed)
.then(result => {
console.log(result);
})
.catch(error => {
console.error('所有都失败:', error.errors); // [Error: 失败1, Error: 失败2]
});
5. 实际应用场景
5.1 异步数据获取
// 封装 fetch 请求为 Promise
function fetchUserData(userId) {
return fetch(`/api/users/${userId}`)
.then(response => {
if (!response.ok) {
throw new Error(`HTTP ${response.status}: ${response.statusText}`);
}
return response.json();
})
.catch(error => {
console.error('获取用户数据失败:', error);
throw error;
});
}
// 获取多个用户数据
function fetchMultipleUsers(userIds) {
const promises = userIds.map(id => fetchUserData(id));
return Promise.all(promises);
}
// 使用示例
fetchUserData(123)
.then(user => {
console.log('用户信息:', user);
})
.catch(error => {
console.error('获取用户失败:', error.message);
});
fetchMultipleUsers([1, 2, 3, 4, 5])
.then(users => {
console.log('所有用户:', users);
})
.catch(error => {
console.error('获取用户列表失败:', error);
});
5.2 图片预加载
// 图片预加载
function preloadImage(src) {
return new Promise((resolve, reject) => {
const img = new Image();
img.onload = () => resolve(img);
img.onerror = () => reject(new Error(`图片加载失败: ${src}`));
img.src = src;
});
}
// 预加载多张图片
function preloadImages(imageSources) {
const promises = imageSources.map(src => preloadImage(src));
return Promise.all(promises);
}
// 使用示例
const images = [
'/images/photo1.jpg',
'/images/photo2.jpg',
'/images/photo3.jpg'
];
preloadImages(images)
.then(loadedImages => {
console.log('所有图片加载完成');
// 显示图片或执行其他操作
loadedImages.forEach((img, index) => {
document.body.appendChild(img);
});
})
.catch(error => {
console.error('图片加载失败:', error.message);
});
5.3 并行和串行执行
// 并行执行多个异步任务
function parallelExecution(tasks) {
return Promise.all(tasks.map(task => task()));
}
// 串行执行多个异步任务
function serialExecution(tasks) {
return tasks.reduce((promise, task) => {
return promise.then(results => {
return task().then(result => [...results, result]);
});
}, Promise.resolve([]));
}
// 使用示例
const tasks = [
() => fetch('/api/task1').then(r => r.json()),
() => fetch('/api/task2').then(r => r.json()),
() => fetch('/api/task3').then(r => r.json())
];
// 并行执行
parallelExecution(tasks)
.then(results => {
console.log('并行执行结果:', results);
});
// 串行执行
serialExecution(tasks)
.then(results => {
console.log('串行执行结果:', results);
});
6. Promise 工具函数
6.1 延迟函数
// 延迟函数
function delay(ms) {
return new Promise(resolve => setTimeout(resolve, ms));
}
// 使用示例
async function example() {
console.log('开始');
await delay(1000);
console.log('1秒后');
await delay(2000);
console.log('再过2秒');
}
example();
6.2 重试机制
// 带重试的 Promise
function retry(promiseFn, maxRetries = 3, delayMs = 1000) {
return new Promise((resolve, reject) => {
const attempt = (retriesLeft) => {
promiseFn()
.then(resolve)
.catch(error => {
if (retriesLeft === 0) {
reject(error);
} else {
console.log(`重试剩余次数: ${retriesLeft}`);
setTimeout(() => {
attempt(retriesLeft - 1);
}, delayMs);
}
});
};
attempt(maxRetries);
});
}
// 使用示例
function unreliableApiCall() {
return fetch('/api/unreliable')
.then(response => {
if (!response.ok) {
throw new Error('API 调用失败');
}
return response.json();
});
}
retry(unreliableApiCall, 3, 1000)
.then(data => {
console.log('API 调用成功:', data);
})
.catch(error => {
console.error('所有重试都失败:', error.message);
});
6.3 超时控制
// Promise 超时控制
function withTimeout(promise, timeoutMs) {
const timeoutPromise = new Promise((resolve, reject) => {
setTimeout(() => reject(new Error('操作超时')), timeoutMs);
});
return Promise.race([promise, timeoutPromise]);
}
// 使用示例
function longRunningTask() {
return new Promise(resolve => {
setTimeout(() => resolve('任务完成'), 5000);
});
}
withTimeout(longRunningTask(), 3000)
.then(result => {
console.log('任务结果:', result);
})
.catch(error => {
if (error.message === '操作超时') {
console.error('任务超时');
} else {
console.error('任务失败:', error);
}
});
7. Promise 与 async/await
7.1 基本转换
// Promise 链式调用
function fetchUserData(userId) {
return fetch(`/api/users/${userId}`)
.then(response => response.json())
.then(user => {
return fetch(`/api/users/${userId}/posts`)
.then(response => response.json())
.then(posts => ({ user, posts }));
});
}
// 转换为 async/await
async function fetchUserDataAsync(userId) {
try {
const userResponse = await fetch(`/api/users/${userId}`);
const user = await userResponse.json();
const postsResponse = await fetch(`/api/users/${userId}/posts`);
const posts = await postsResponse.json();
return { user, posts };
} catch (error) {
console.error('获取用户数据失败:', error);
throw error;
}
}
7.2 错误处理对比
// Promise 方式
function processData() {
return fetch('/api/data')
.then(response => response.json())
.then(data => {
return processStep1(data);
})
.then(result1 => {
return processStep2(result1);
})
.then(result2 => {
return processStep3(result2);
})
.catch(error => {
console.error('处理失败:', error);
return { error: error.message };
});
}
// async/await 方式
async function processDataAsync() {
try {
const response = await fetch('/api/data');
const data = await response.json();
const result1 = await processStep1(data);
const result2 = await processStep2(result1);
const result3 = await processStep3(result2);
return result3;
} catch (error) {
console.error('处理失败:', error);
return { error: error.message };
}
}
8. Promise 最佳实践
8.1 错误处理最佳实践
// 好的做法:明确的错误处理
function goodPractice() {
return fetch('/api/data')
.then(response => {
if (!response.ok) {
throw new Error(`HTTP ${response.status}`);
}
return response.json();
})
.then(data => {
// 处理业务逻辑错误
if (!data.isValid) {
throw new Error('数据无效');
}
return data;
})
.catch(error => {
// 记录错误日志
console.error('API 调用失败:', error);
// 返回默认值或重新抛出错误
return { error: error.message, data: null };
});
}
// 避免:忽略错误处理
function badPractice() {
return fetch('/api/data')
.then(response => response.json()); // 如果 fetch 失败怎么办?
}
8.2 Promise 链优化
// 优化前:嵌套的 Promise 链
function beforeOptimization() {
return fetch('/api/user')
.then(response => response.json())
.then(user => {
return fetch(`/api/posts/${user.id}`)
.then(response => response.json())
.then(posts => {
return fetch(`/api/comments/${posts[0].id}`)
.then(response => response.json())
.then(comments => {
return { user, posts, comments };
});
});
});
}
// 优化后:扁平化的 Promise 链
function afterOptimization() {
let userData, postsData;
return fetch('/api/user')
.then(response => response.json())
.then(user => {
userData = user;
return fetch(`/api/posts/${user.id}`);
})
.then(response => response.json())
.then(posts => {
postsData = posts;
return fetch(`/api/comments/${posts[0].id}`);
})
.then(response => response.json())
.then(comments => {
return { user: userData, posts: postsData, comments };
});
}
// 最佳:使用 async/await
async function bestPractice() {
try {
const userResponse = await fetch('/api/user');
const user = await userResponse.json();
const postsResponse = await fetch(`/api/posts/${user.id}`);
const posts = await postsResponse.json();
const commentsResponse = await fetch(`/api/comments/${posts[0].id}`);
const comments = await commentsResponse.json();
return { user, posts, comments };
} catch (error) {
console.error('数据获取失败:', error);
throw error;
}
}
8.3 性能优化
// 批量请求优化
class ApiBatcher {
constructor(delay = 50) {
this.queue = [];
this.delay = delay;
this.timer = null;
}
add(request) {
return new Promise((resolve, reject) => {
this.queue.push({ request, resolve, reject });
if (this.timer) {
clearTimeout(this.timer);
}
this.timer = setTimeout(() => {
this.processBatch();
}, this.delay);
});
}
async processBatch() {
if (this.queue.length === 0) return;
const batch = [...this.queue];
this.queue = [];
this.timer = null;
try {
const requests = batch.map(item => item.request);
const response = await fetch('/api/batch', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ requests })
});
const results = await response.json();
results.forEach((result, index) => {
if (result.success) {
batch[index].resolve(result.data);
} else {
batch[index].reject(new Error(result.error));
}
});
} catch (error) {
batch.forEach(item => item.reject(error));
}
}
}
// 使用批处理
const batcher = new ApiBatcher();
Promise.all([
batcher.add({ type: 'getUser', id: 1 }),
batcher.add({ type: 'getUser', id: 2 }),
batcher.add({ type: 'getPost', id: 1 })
]).then(results => {
console.log('批量请求结果:', results);
});
9. Promise 调试技巧
9.1 调试 Promise 链
// 添加调试信息
function debugPromise(promise, label) {
return promise
.then(result => {
console.log(`${label} - 成功:`, result);
return result;
})
.catch(error => {
console.error(`${label} - 失败:`, error);
throw error;
});
}
// 使用示例
debugPromise(fetch('/api/data'), '获取数据')
.then(response => debugPromise(response.json(), '解析数据'))
.then(data => debugPromise(processData(data), '处理数据'))
.catch(error => console.error('整体流程失败:', error));
9.2 Promise 状态监控
// Promise 状态监控
class PromiseMonitor {
constructor() {
this.pending = 0;
this.fulfilled = 0;
this.rejected = 0;
}
track(promise) {
this.pending++;
console.log(`Pending promises: ${this.pending}`);
return promise
.then(result => {
this.pending--;
this.fulfilled++;
console.log(`Promise fulfilled. Pending: ${this.pending}, Fulfilled: ${this.fulfilled}`);
return result;
})
.catch(error => {
this.pending--;
this.rejected++;
console.log(`Promise rejected. Pending: ${this.pending}, Rejected: ${this.rejected}`);
throw error;
});
}
}
// 使用监控
const monitor = new PromiseMonitor();
monitor.track(fetch('/api/data1'));
monitor.track(fetch('/api/data2'));
Promise 是现代 JavaScript 异步编程的核心,掌握其使用方法和最佳实践对于开发高质量的 Web 应用至关重要。通过合理使用 Promise,可以写出更加清晰、可维护的异步代码。
Promise 详解与应用全掌握
86

被折叠的 条评论
为什么被折叠?



