在JavaScript中,Promise
、async
和await
是用于处理异步操作的核心概念。它们帮助我们更好地组织异步代码,避免回调嵌套问题,并使代码更简洁和易读。
1. Promise
Promise
是一种异步编程的解决方案,用于表示一个操作的最终完成(或失败),以及它所产生的结果。
-
状态:
pending
:Promise 初始状态,既没有被解决也没有被拒绝。fulfilled
:操作成功完成,Promise 已被解决。rejected
:操作失败,Promise 被拒绝。
-
用法:
then()
:当Promise成功时执行回调。catch()
:当Promise被拒绝时执行回调。finally()
:无论Promise成功或失败,都会执行的回调。
创建一个Promise:
let promise = new Promise((resolve, reject) => {let success = true;if (success) {resolve("Operation succeeded!");} else {reject("Operation failed.");}
});promise.then(result => console.log(result)) // 如果成功,输出 "Operation succeeded!".catch(error => console.log(error)) // 如果失败,输出 "Operation failed.".finally(() => console.log("Operation completed."));
下面是一个更实际的 Promise
示例,模拟了从数据库中获取用户数据的场景。假设我们有一个函数 getUserData
,该函数通过异步操作从“数据库”中获取用户信息,并返回一个 Promise
。在操作过程中,可能会成功获取数据,也可能会发生错误(如数据库连接失败)。
示例代码:
// 模拟数据库操作的函数,返回一个 Promise
function getUserData(userId) {return new Promise((resolve, reject) => {console.log(`Fetching data for user with ID: ${userId}...`);// 模拟异步操作,假设操作时间为2秒setTimeout(() => {const userDatabase = {1: { name: 'Alice', age: 25, city: 'New York' },2: { name: 'Bob', age: 30, city: 'San Francisco' },3: { name: 'Charlie', age: 35, city: 'London' }};const userData = userDatabase[userId];if (userData) {resolve(userData); // 成功获取数据,调用 resolve} else {reject(`No user found with ID: ${userId}`); // 未找到用户,调用 reject}}, 2000);});
}// 使用 Promise 获取用户数据
getUserData(2).then(userData => {console.log('User data:', userData); // 成功时输出用户数据}).catch(error => {console.error('Error:', error); // 失败时输出错误信息}).finally(() => {console.log('Database operation completed.'); // 无论成功与否,都会执行});
解释:
-
getUserData
函数:- 模拟了一个异步的数据库查询操作,通过
setTimeout
模拟延迟。 - 在2秒后,检查一个假设的用户数据库
userDatabase
,如果找到相应的用户数据,就调用resolve
来返回数据;如果没有找到,则调用reject
返回错误信息。
- 模拟了一个异步的数据库查询操作,通过
-
Promise 的使用:
- 通过
then()
方法处理成功的结果,即当用户数据被成功获取时,输出用户数据。 - 通过
catch()
方法处理失败的结果,即当未找到用户时,输出错误信息。 - 通过
finally()
方法处理无论成功或失败都会执行的操作,比如清理资源或结束操作的提示。
- 通过
2. async/await
async/await
是基于 Promise
的更高层抽象,目的是让异步代码看起来像同步代码,从而提高可读性和简洁性。
async
:- 关键字
async
用于声明一个异步函数,异步函数总是返回一个Promise
。 - 如果返回值不是
Promise
,JavaScript 会将其包装为已解决的Promise
。
- 关键字
async function example() {return "Hello, World!";
}example().then(result => console.log(result)); // 输出: Hello, World!
await
:await
只能在async
函数中使用,用来等待一个异步操作的结果。- 当
await
一个 Promise 时,函数暂停执行,直到 Promise 完成,并返回结果。 - 它消除了
.then()
链式调用的复杂性,使代码更清晰。
async function fetchData() {let response = await fetch('https://api.example.com/data');let data = await response.json();return data;
}fetchData().then(data => console.log(data));
在这个例子中,await
会等待 fetch
请求的 Promise 完成,然后继续执行后续代码。
3. Promise 和 async/await 的对比
- Promise 的写法是链式的,使用
.then()
、.catch()
和.finally()
,它适合多个异步操作的顺序处理,但当嵌套过深时,代码可能会变得难以维护。
function getData() {return fetch('https://api.example.com/data').then(response => response.json()).then(data => {console.log(data);}).catch(error => {console.error(error);});
}
- async/await 通过让代码看起来像同步的方式解决了回调地狱问题。它更加直观且容易处理错误。
async function getData() {try {let response = await fetch('https://api.example.com/data');let data = await response.json();console.log(data);} catch (error) {console.error(error);}
}
4. 错误处理
在 Promise
中,使用 .catch()
来捕获错误:
fetch('https://api.example.com/data').then(response => response.json()).catch(error => console.error('Error:', error));
在 async/await
中,使用 try/catch
来捕获错误:
async function fetchData() {try {let response = await fetch('https://api.example.com/data');let data = await response.json();return data;} catch (error) {console.error('Error:', error);}
}
总结
async/await
是基于 Promise
的语法糖,使用它们可以让代码更加整洁且易于维护。