概要
在JavaScript中,异步编程是一个绕不开的话题。无论是网络请求、定时器、文件读写还是其他I/O操作,这些异步任务都需要我们以一种特殊的方式来处理。Promise和Async/Await就是为解决这些问题而诞生的两个重要概念。
Promise
Promise的原理与作用
Promise是es6中引入的一个新的对象,用于表示一个异步操作的最终完成(或失败)及其结果值。
它有三种状态:pending(进行中)、fulfilled(已成功)和rejected(已失败)。
**一旦Promise的状态从pending变为fulfilled或rejected,就不会再改变。**
Promise的主要作用在于:
-
解决回调地狱:传统的异步编程方式经常导致回调函数的嵌套层级过深,形成所谓的“回调地狱”。Promise通过链式调用的方式,使得我们可以将异步操作平铺开来,提高了代码的可读性和可维护性。
- 传统的异步编程方式,在回调函数中,又将去调用新的回调函数。每个异步操作都依赖于前一个操作的结果,因此回调函数不得不嵌套在一起。
-
更好的错误处理:Promise提供了.catch()方法,用于捕获异步操作过程中发生的错误。这使得错误处理更加集中和统一,避免了错误被层层传递或遗漏的情况。
-
更灵活的异步操作组合:Promise支持多个异步操作的组合,比如通过Promise.all()等待所有异步操作完成,或者通过Promise.race()只等待第一个完成的异步操作。
Promise的返回值
- resolve函数用于将Promise对象的状态从pending变为fulfilled,并传递一个值给后续的.then()方法。
- reject函数用于将Promise对象的状态从pending变为rejected,并传递一个错误原因给后续的.catch()方法。
const myPromise = new Promise((resolve, reject) => {
// 模拟异步操作,比如网络请求或定时器
setTimeout(() => {
// // // // 若异步操作成功,调用resolve函数并传递一个值
resolve('异步操作成功,这是返回的结果!');
// // // // 若异步操作失败,调用reject函数并传递一个错误原因
// reject('异步操作失败,这是失败的原因!');
}, 1000);
});
// 使用.then()方法处理Promise成功的情况
myPromise.then(result => {
console.log(result); // 输出:异步操作成功,这是返回的结果!
}).catch(error => {
console.error(error); // 如果Promise被拒绝,这里的代码会执行
});
Promise常用方法
- .then(onFulfilled, onRejected):指定异步操作成功时要执行的回调函数onFulfilled,以及失败时要执行的回调函数onRejected。这两个参数都是可选的。
- .catch(onRejected):指定异步操作失败时要执行的回调函数。它实际上是.then(undefined, onRejected)的语法糖。
- .finally(onFinally):无论异步操作成功还是失败,都会执行的回调函数。它用于执行一些清理工作或日志记录等。
- Promise.all(promises):接受一个Promise对象的数组作为参数,并返回一个新的Promise对象。只有当数组中的所有Promise对象都变为fulfilled状态时,新的Promise对象才会变为fulfilled,并把所有Promise对象的结果作为一个数组返回。
- Promise.race(promises):接受一个Promise对象的数组作为参数,并返回一个新的Promise对象。但只要数组中有一个Promise对象变为fulfilled或rejected,新的Promise对象就会立即变为相应的状态,并返回那个Promise对象的结果或错误原因。
- Promise.any(promises):它会等待给定的 Promise 数组中的任何一个 Promise 完成。一旦有一个 Promise 完成,Promise.any() 也会完成,并返回那个 Promise 的结果。如果所有 Promise 都失败,则 Promise.any() 会失败,并返回一个包含所有失败原因的 AggregateError。
- resolve
- reject
Async/Await
s虽然Promise极大地改善了异步编程体验,但书写和理解Promise链仍然有一定的学习成本。于是,ES2017引入了Async/Await,使得异步代码可以像同步代码一样书写和阅读。
优点: 使得异步代码可以像同步代码一样书写和阅读。
async函数
在函数声明或函数表达式前加上async关键字,表示该函数为一个异步任务,不会阻塞后面函数的执行。
await表达式
只能在async函数内部使用。await后面跟一个Promise对象,它会“等待”这个Promise对象变为fulfilled状态,并返回其结果。如果Promise对象变为rejected状态,await会抛出异常。
例如:
// 定义一个返回Promise的异步函数
function fetchData() {
return new Promise((resolve, reject) => {
setTimeout(() => {
if (Math.random() > 0.5) {
resolve('成功获取数据!');
} else {
reject('获取数据失败!');
}
}, 1000);
};
}
// 使用async/await的异步函数
async function processData() {
try {
const data = await fetchData(); // 等待fetchData的Promise完成
console.log(data); // 如果成功,输出 '成功获取数据!'
} catch (error) {
console.error(error); // 如果失败,输出 '获取数据失败!'
}
}
processData();