JavaScript Promise深入解析与实战技巧

Promise 是 JavaScript 中处理异步操作的重要机制,掌握它对于编写高质量的异步代码至关重要。
一、Promise 基础概念
1.1 什么是 Promise
Promise 是一个代表异步操作最终完成或失败的对象。它有三种状态:
1 2 3 4
| const pending = 'pending'; const fulfilled = 'fulfilled'; const rejected = 'rejected';
|
特点:
- 状态不可逆,一旦改变就不会再变
- 只有异步操作的结果可以决定当前状态
1.2 创建 Promise
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| const promise = new Promise((resolve, reject) => { setTimeout(() => { if (Math.random() > 0.5) { resolve('操作成功'); } else { reject('操作失败'); } }, 1000); });
promise .then(result => console.log(result)) .catch(error => console.error(error));
|
二、Promise 的链式调用
2.1 then 方法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| fetchUser() .then(user => { console.log('用户信息:', user); return fetchPosts(user.id); }) .then(posts => { console.log('用户文章:', posts); return fetchComments(posts[0].id); }) .then(comments => { console.log('文章评论:', comments); }) .catch(error => { console.error('发生错误:', error); });
|
2.2 then 的返回值
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27
| Promise.resolve(1) .then(value => { return value + 1; }) .then(value => { console.log(value); return Promise.resolve(value + 1); }) .then(value => { console.log(value); throw new Error('出错了'); }) .catch(error => { console.error(error.message); return '恢复后的值'; }) .then(value => { console.log(value); });
|
三、Promise 静态方法
3.1 Promise.all
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30
| const promises = [ Promise.resolve(1), Promise.resolve(2), Promise.resolve(3) ];
Promise.all(promises) .then(results => { console.log(results); }) .catch(error => { console.error(error); });
async function fetchDashboardData() { try { const [user, posts, stats] = await Promise.all([ fetch('/api/user').then(r => r.json()), fetch('/api/posts').then(r => r.json()), fetch('/api/stats').then(r => r.json()) ]); return { user, posts, stats }; } catch (error) { console.error('加载仪表盘数据失败:', error); } }
|
3.2 Promise.race
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30
| const racePromise = Promise.race([ fetch('/api/fast'), fetch('/api/slow') ]);
racePromise.then(response => { console.log('最先完成的响应:', response); });
function fetchWithTimeout(url, timeout = 5000) { return Promise.race([ fetch(url), new Promise((_, reject) => { setTimeout(() => { reject(new Error('请求超时')); }, timeout); }) ]); }
fetchWithTimeout('/api/data') .then(response => response.json()) .catch(error => { if (error.message === '请求超时') { console.log('请求超时,请重试'); } });
|
3.3 Promise.allSettled
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| const promises = [ Promise.resolve(1), Promise.reject('error'), Promise.resolve(3) ];
Promise.allSettled(promises).then(results => { results.forEach(result => { if (result.status === 'fulfilled') { console.log('成功:', result.value); } else { console.log('失败:', result.reason); } }); });
|
3.4 Promise.any
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| const promises = [ Promise.reject('error1'), Promise.resolve('success'), Promise.reject('error2') ];
Promise.any(promises) .then(result => { console.log(result); }) .catch(error => { console.error('全部失败:', error); });
|
四、async/await 语法糖
4.1 基本用法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| async function fetchData() { const response = await fetch('/api/data'); const data = await response.json(); return data; }
function fetchData() { return fetch('/api/data') .then(response => response.json()); }
fetchData().then(data => console.log(data));
|
4.2 错误处理
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| async function handleRequest() { try { const user = await fetchUser(); const posts = await fetchPosts(user.id); return posts; } catch (error) { console.error('请求失败:', error); return []; } }
async function handleRequest() { const posts = await fetchPosts() .catch(error => { console.error('请求失败:', error); return []; }); return posts; }
|
4.3 并发请求
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| async function getPosts() { const post1 = await fetchPost(1); const post2 = await fetchPost(2); const post3 = await fetchPost(3); return [post1, post2, post3]; }
async function getPosts() { const [post1, post2, post3] = await Promise.all([ fetchPost(1), fetchPost(2), fetchPost(3) ]); return [post1, post2, post3]; }
|
五、实战技巧
5.1 请求重试机制
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
| async function fetchWithRetry(url, options = {}, maxRetries = 3) { let retryCount = 0; while (retryCount < maxRetries) { try { const response = await fetch(url, options); if (!response.ok) { throw new Error(`HTTP ${response.status}`); } return await response.json(); } catch (error) { retryCount++; if (retryCount >= maxRetries) { throw new Error(`请求失败,已重试 ${maxRetries} 次`); } const delay = Math.pow(2, retryCount) * 1000; await new Promise(resolve => setTimeout(resolve, delay)); } } }
|
5.2 请求缓存
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| const cache = new Map();
async function fetchWithCache(url, ttl = 60000) { const cached = cache.get(url); if (cached && Date.now() - cached.timestamp < ttl) { console.log('使用缓存:', url); return cached.data; } const data = await fetch(url).then(r => r.json()); cache.set(url, { data, timestamp: Date.now() }); return data; }
|
5.3 请求队列
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46
| class RequestQueue { constructor(maxConcurrent = 5) { this.maxConcurrent = maxConcurrent; this.queue = []; this.running = 0; } add(promiseFactory) { return new Promise((resolve, reject) => { this.queue.push({ promiseFactory, resolve, reject }); this.process(); }); } async process() { if (this.running >= this.maxConcurrent || this.queue.length === 0) { return; } this.running++; const { promiseFactory, resolve, reject } = this.queue.shift(); try { const result = await promiseFactory(); resolve(result); } catch (error) { reject(error); } finally { this.running--; this.process(); } } }
const queue = new RequestQueue(3);
for (let i = 0; i < 10; i++) { queue.add(() => fetch(`/api/item/${i}`).then(r => r.json())) .then(data => console.log(`Item ${i}:`, data)) .catch(error => console.error(`Item ${i} 失败:`, error)); }
|
六、常见陷阱
6.1 忘记 return
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| fetchUser() .then(user => { fetchPosts(user.id); }) .then(posts => { console.log(posts); });
fetchUser() .then(user => { return fetchPosts(user.id); }) .then(posts => { console.log(posts); });
|
6.2 在回调中嵌套 Promise
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| fetchUser() .then(user => { fetchPosts(user.id) .then(posts => { fetchComments(posts[0].id) .then(comments => { console.log(comments); }); }); });
fetchUser() .then(user => fetchPosts(user.id)) .then(posts => fetchComments(posts[0].id)) .then(comments => console.log(comments));
|
6.3 忽略错误
1 2 3 4 5 6 7 8 9 10
| fetch('/api/data') .then(response => response.json()) .then(data => console.log(data));
fetch('/api/data') .then(response => response.json()) .then(data => console.log(data)) .catch(error => console.error(error));
|
七、最佳实践
7.1 总是使用 async/await
1 2 3 4 5 6 7 8 9 10 11
| async function loadData() { try { const user = await fetchUser(); const posts = await fetchPosts(user.id); return posts; } catch (error) { console.error(error); return []; } }
|
7.2 合理使用 Promise.all
1 2 3 4 5 6 7 8 9 10
| async function loadPageData() { const [user, posts, comments] = await Promise.all([ fetchUser(), fetchPosts(), fetchComments() ]); return { user, posts, comments }; }
|
7.3 错误处理要具体
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| async function handleRequest() { try { const data = await fetchData(); return data; } catch (error) { if (error instanceof NetworkError) { console.log('网络错误,请检查连接'); } else if (error instanceof AuthError) { console.log('认证失败,请重新登录'); } else { console.error('未知错误:', error); } throw error; } }
|
总结
Promise 是现代 JavaScript 异步编程的核心,掌握它对于编写高质量的代码至关重要:
- 理解 Promise 的三种状态和状态不可逆特性
- 熟练使用 then、catch 进行链式调用
- 掌握 Promise.all、Promise.race 等静态方法
- 优先使用 async/await 提高代码可读性
- 注意常见陷阱,遵循最佳实践
持续学习,不断进步!