Promise 详解速览

Promise 详解与应用全掌握

Promise 详解速览

1. Promise 概述

Promise 是 JavaScript 中处理异步操作的一种方式,它代表了一个异步操作的最终完成(或失败)及其结果值。

核心概念:

  • 状态管理:Promise 有三种状态 - pending(待定)、fulfilled(已成功)、rejected(已失败)
  • 不可逆性:一旦状态改变,就不能再改变
  • 链式调用:支持链式调用,避免回调地狱
  • 错误处理:统一的错误处理机制

Promise 状态:

  1. Pending(待定):初始状态,既没有被兑现,也没有被拒绝
  2. Fulfilled(已成功):操作成功完成
  3. 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,可以写出更加清晰、可维护的异步代码。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值