Promise 是 JavaScript 中处理异步操作的一种对象,它代表了一个异步操作的最终完成(或失败)及其结果值。Promise 是 ES6 引入的特性,用于解决传统回调函数嵌套过深导致的"回调地狱"问题。
一、Promise 的基本概念
Promise 是一个对象,它有三种状态:
pending(等待中):初始状态,既未成功也未失败。
fulfilled(已成功):操作成功完成。
rejected(已失败):操作失败。
状态的特点:
1. 状态只能从 pending 转变为 fulfilled 或 rejected。
2. 状态一旦改变,就会永久保持该状态,不会再发生变化。

二、Promise 的创建与使用
1. 创建 Promise
const promise = new Promise((resolve, reject) => {
// 异步操作
setTimeout(() => {
const success = true;
if (success) {
resolve('操作成功!'); // 将状态从 pending 改为 fulfilled
} else {
reject('操作失败!'); // 将状态从 pending 改为 rejected
}
}, 1000);
});Promise 构造函数接受一个函数作为参数,该函数的两个参数分别是 resolve 和 reject:
resolve 函数:将 Promise 对象的状态从 pending 变为 fulfilled,并将异步操作的结果作为参数传递出去。
reject 函数:将 Promise 对象的状态从 pending 变为 rejected,并将异步操作报出的错误作为参数传递出去。
创建 Promise 对象时必须附带这个执行器回调函数,即:
// 正确:带有执行器函数
const promise = new Promise((resolve, reject) => {
// 异步操作代码
});// 错误:没有提供执行器函数
const invalidPromise = new Promise(); // 会抛出 TypeError2. Promise 实例的方法
then() 方法
then() 方法用于处理 Promise 成功或失败时的回调函数,它返回一个新的 Promise 对象,因此可以链式调用。
promise.then(
value => {
console.log('成功:', value);
},
reason => {
console.log('失败:', reason);
}
);catch() 方法
catch() 方法用于指定发生错误时的回调函数,它是 .then(null, rejection) 的别名。
promise
.then(value => {
console.log('成功:', value);
})
.catch(reason => {
console.log('失败:', reason);
});finally() 方法
finally() 方法用于指定无论 Promise 是成功还是失败都会执行的回调函数。
promise
.then(value => {
console.log('成功:', value);
})
.catch(reason => {
console.log('失败:', reason);
})
.finally(() => {
console.log('无论成功失败都会执行');
});三、Promise 的静态方法
1. Promise.resolve()
创建一个立即 resolved 的 Promise 对象。
const resolvedPromise = Promise.resolve('成功');
resolvedPromise.then(value => console.log(value)); // 输出: 成功2. Promise.reject()
创建一个立即 rejected 的 Promise 对象。
const rejectedPromise = Promise.reject('失败');
rejectedPromise.catch(reason => console.log(reason)); // 输出: 失败3. Promise.all()
将多个 Promise 实例包装成一个新的 Promise 实例。只有当所有 Promise 都成功时,返回的 Promise 才会成功;如果有任何一个 Promise 失败,返回的 Promise 就会失败。
const promise1 = Promise.resolve(1);
const promise2 = Promise.resolve(2);
const promise3 = Promise.resolve(3);
Promise.all([promise1, promise2, promise3]).then(values => {
console.log(values); // 输出: [1, 2, 3]
});4. Promise.race()
将多个 Promise 实例包装成一个新的 Promise 实例。只要有一个 Promise 状态发生改变(无论是成功还是失败),返回的 Promise 就会跟着改变状态。
const promise1 = new Promise(resolve => setTimeout(() => resolve('one'), 1000));
const promise2 = new Promise(resolve => setTimeout(() => resolve('two'), 500));
Promise.race([promise1, promise2]).then(value => {
console.log(value); // 输出: 'two',因为它更快
});5. Promise.allSettled()
将多个 Promise 实例包装成一个新的 Promise 实例。只有当所有 Promise 都结束时(无论成功或失败),返回的 Promise 才会结束,返回所有 Promise 的结果数组。
const promise1 = Promise.resolve(1);
const promise2 = Promise.reject('错误');
Promise.allSettled([promise1, promise2]).then(results => {
console.log(results);
// 输出:
// [
// { status: 'fulfilled', value: 1 },
// { status: 'rejected', reason: '错误' }
// ]
});6. Promise.any()
将多个 Promise 实例包装成一个新的 Promise 实例。只要有一个 Promise 成功,返回的 Promise 就会成功;只有当所有 Promise 都失败时,返回的 Promise 才会失败。
const promise1 = Promise.reject('错误1');
const promise2 = Promise.resolve('成功');
const promise3 = Promise.reject('错误3');
Promise.any([promise1, promise2, promise3]).then(value => {
console.log(value); // 输出: '成功'
});四、Promise 解决回调地狱问题
传统的回调函数嵌套可能会导致代码难以阅读和维护:
// 回调地狱示例
asyncOperation1(function(result1) {
asyncOperation2(result1, function(result2) {
asyncOperation3(result2, function(result3) {
// 更多嵌套回调...
});
});
});使用 Promise 链式调用可以大大提高代码的可读性:
// Promise 链式调用示例
asyncOperation1()
.then(result1 => asyncOperation2(result1))
.then(result2 => asyncOperation3(result2))
.then(result3 => {
// 处理最终结果
})
.catch(error => {
// 处理所有错误
});五、Promise 的实际应用场景
1. 处理网络请求
fetch('https://api.example.com/data')
.then(response => response.json())
.then(data => console.log(data))
.catch(error => console.error('请求失败:', error));2. 定时器操作
const delay = ms => new Promise(resolve => setTimeout(resolve, ms));
delay(2000)
.then(() => console.log('延迟了2秒'));3. 并行异步操作
const fetchUser = () => fetch('https://api.example.com/user');
const fetchPosts = () => fetch('https://api.example.com/posts');
Promise.all([fetchUser(), fetchPosts()])
.then(([userResponse, postsResponse]) => Promise.all([userResponse.json(), postsResponse.json()]))
.then(([userData, postsData]) => {
console.log('用户数据:', userData);
console.log('文章数据:', postsData);
});总结
Promise 是 JavaScript 处理异步操作的强大工具,它通过链式调用解决了回调地狱问题,使异步代码更加清晰易读。掌握 Promise 的各种方法和用法,对于编写现代 JavaScript 应用至关重要。在实际开发中,Promise 通常与 async/await 结合使用,可以进一步简化异步代码的编写。
下面是关于async/await的相关内容。
在实际开发中,async/await 是 ES2017 引入的语法糖,它基于 Promise,能够让异步代码的写法更接近同步代码,极大提高了代码的可读性和可维护性。下面通过示例来说明它们的结合使用:
1. 基本用法
// 1. 定义一个返回 Promise 的函数
function fetchData(url) {
return new Promise((resolve, reject) => {
// 模拟网络请求
setTimeout(() => {
if (url) {
resolve(`数据来自 ${url}`);
} else {
reject(new Error('URL不能为空'));
}
}, 1000);
});
}
// 2. 使用 async/await 简化异步代码
async function getData() {
try {
// 使用 await 等待 Promise 完成
const result = await fetchData('https://api.example.com');
console.log(result); // 输出: 数据来自 https://api.example.com
return result;
} catch (error) {
// 错误处理更直观
console.error('获取数据失败:', error.message);
}
}
// 调用异步函数
getData();2. 串行执行多个异步操作
async function fetchMultipleData() {
try {
// 串行执行,第二个请求依赖第一个请求的结果
const userData = await fetchData('https://api.example.com/users');
console.log('用户数据获取完成');
// 基于用户数据获取订单数据
const orderData = await fetchData(`https://api.example.com/orders?user=${userData.id || 'default'}`);
console.log('订单数据获取完成');
return { userData, orderData };
} catch (error) {
console.error('获取数据失败:', error);
}
}
fetchMultipleData();3. 并行执行多个异步操作
async function fetchParallelData() {
try {
// 使用 Promise.all 并行执行多个异步操作
const [userData, productData, commentData] = await Promise.all([
fetchData('https://api.example.com/users'),
fetchData('https://api.example.com/products'),
fetchData('https://api.example.com/comments')
]);
console.log('所有数据获取完成');
return { userData, productData, commentData };
} catch (error) {
console.error('至少有一个请求失败:', error);
}
}
fetchParallelData();4. 实际开发场景示例(模拟用户登录流程)
// 模拟登录 API
function login(username, password) {
return new Promise((resolve, reject) => {
setTimeout(() => {
if (username === 'admin' && password === '123456') {
resolve({ token: 'jwt-token-123', userId: 1 });
} else {
reject(new Error('用户名或密码错误'));
}
}, 800);
});
}
// 模拟获取用户信息 API
function getUserInfo(userId, token) {
return new Promise((resolve, reject) => {
setTimeout(() => {
if (token && userId) {
resolve({
id: userId,
name: '管理员',
role: 'admin',
permissions: ['read', 'write', 'delete']
});
} else {
reject(new Error('无效的认证信息'));
}
}, 600);
});
}
// 使用 async/await 简化登录流程
async function userLoginProcess(username, password) {
try {
// 1. 登录认证
console.log('开始登录...');
const loginResult = await login(username, password);
console.log('登录成功,获取用户信息...');
// 2. 获取用户详情
const userInfo = await getUserInfo(loginResult.userId, loginResult.token);
console.log('用户信息获取成功');
// 3. 返回完整结果
return { ...loginResult, ...userInfo };
} catch (error) {
console.error('登录流程失败:', error.message);
throw error; // 可以选择继续抛出错误,让调用者处理
}
}
// 调用登录流程
userLoginProcess('admin', '123456')
.then(result => {
console.log('登录流程完成,用户数据:', result);
})
.catch(error => {
console.log('最终错误处理:', error);
});5. async/await 的优势总结
1. 代码更简洁:不需要嵌套的 .then() 和 .catch() 链。
2. 错误处理更直观:可以使用传统的 try/catch 结构。
3. 流程控制更清晰:异步代码的执行顺序一目了然。
4. 调试更方便:可以像调试同步代码一样设置断点。
5. 条件和循环处理更自然:在异步代码中使用条件判断和循环结构更加直观。
通过这些示例可以看出,async/await 让异步代码的编写和阅读变得更加自然,是现代 JavaScript 开发中处理异步操作的首选方式。