前言
在前端开发中,异步编程是绕不开的核心知识点。从早期的回调函数到 ES6 推出的 Promise,再到 ES7 新增的 async/await 语法糖,异步编程的体验不断优化。本文将带你系统梳理 Promise 与 async/await 的核心知识点,帮你搞懂异步编程的实现逻辑。
一、从同步到异步:Promise 的登场
在介绍 Promise 之前,我们先看一个同步的字符串验证函数,直观感受同步代码的局限性。
1. 同步验证的痛点
下面的同步函数用于校验字符串数组是否仅包含英文字符,一旦遇到不符合条件的元素就会立即返回错误:
javascript
运行
const batchValidateSecretsSync = (secretsArray) => {
const englishOnlyRegex = /^[a-zA-Z]+$/;
const passedResults = [];
for (const item of secretsArray) {
if (typeof item !== 'string') {
return {
success: false,
message: `验证失败:发现非字符串类型的值 "${item}"。`
};
}
if (englishOnlyRegex.test(item)) {
passedResults.push(item);
} else {
return {
success: false,
message: `验证失败:字符串 "${item}" 包含非英文字符 (如数字或特殊符号)。`
};
}
}
return {
success: true,
message: "所有秘密字符串均通过验证。",
results: passedResults
};
};
同步代码的问题在于会阻塞后续逻辑执行,且无法高效处理多个异步任务。而 Promise 则能很好地解决这些问题,我们可以将上述逻辑改造成异步验证:
javascript
运行
const batchValidateSecrets = (secretsArray) => {
const createValidationPromise = (inputString) => new Promise((resolve, reject) => {
const englishOnlyRegex = /^[a-zA-Z]+$/;
if (typeof inputString !== 'string') {
reject('错误:输入必须是一个字符串。');
return;
}
if (englishOnlyRegex.test(inputString)) {
resolve(inputString);
} else {
reject(`错误:字符串'${inputString}'只能包含英文字符,不能包含数字、空格或其他特殊字符。`);
}
});
const validationPromises = secretsArray.map(secret => createValidationPromise(secret));
return Promise.all(validationPromises);
};
二、Promise 核心知识点
1. Promise 的三种状态
Promise 存在三种互斥的状态,且状态一旦改变就无法逆转:
- pending:进行中,初始状态
- fulfilled:成功,异步操作完成
- rejected:失败,异步操作出错
示例代码如下:
javascript
运行
const arr = new Promise(
(resolve,reject)=>{
if(false){
resolve(1)//将promise状态变为fulfilled
}else{
reject('错误')//将promise状态变为rejected
}
}
)
// 捕获错误
arr.catch(reason => {
console.error("成功捕获到错误:", reason);
});
console.log(arr)
2. then 与 catch 方法
- then:可以同时处理成功(fulfilled)和失败(rejected)两种状态,第一个参数接收成功结果,第二个参数接收错误原因
- catch:专门用于捕获 Promise 链中的错误,若 Promise 未做错误捕获会触发全局报错
javascript
运行
arr.then((result)=>{
console.log('成功',result)
},(error) =>{
console.log('失败',error)
})
arr.catch(reason => {
console.error("成功捕获到错误:", reason);
});
3. Promise 的静态方法
Promise 提供了多个实用静态方法,用于协调多个异步任务:
-
Promise.all() :接收 Promise 数组,只有所有 Promise 都成功才返回结果数组,只要有一个失败就立即触发错误
javascript
运行
const promise1 = Promise.resolve("请求1成功"); const promise2 = new Promise((resolve, reject) => { setTimeout(() => reject(new Error("请求2失败")), 2000); }); const promise3 = Promise.resolve("请求3成功"); Promise.all([promise1, promise2, promise3]) .then((results) => { console.log("所有请求成功:", results); }) .catch((error) => { console.log("请求失败:", error.message); // 输出:请求失败:请求2失败 }); -
Promise.any() :等待第一个成功的 Promise 并返回其结果,仅当所有 Promise 都失败时才抛出
AggregateErrorjavascript
运行
const promise1 = new Promise((resolve) => setTimeout(() => resolve('第一个成功'), 1000)); const promise2 = new Promise((resolve) => setTimeout(() => resolve('第二个成功'), 500)); Promise.any([promise1,promise2]).then((result)=>{ console.log(result) // 输出:第二个成功 }) -
Promise.race() :返回第一个完成的 Promise 结果,无论该结果是成功还是失败
javascript
运行
const promise1 = new Promise((resolve, reject) => { setTimeout(() => reject("one"), 1000); }); const promise2 = new Promise((resolve, reject) => { setTimeout(() => reject("two"), 100); }); Promise.race([promise1, promise2]) .catch((reason) => { console.log("拒绝原因:", reason); // 输出:two }); -
Promise.resolve()/Promise.reject() :快速创建成功 / 失败状态的 Promise 实例
javascript
运行
const successPromise = Promise.resolve("操作成功:数据获取完成"); successPromise.then((result) => { console.log("成功结果:", result); }); const rejectedPromise = Promise.reject("请求失败:网络超时"); rejectedPromise.catch((err) => { console.log("捕获到错误:", err); });
除此之外,还有 Promise.try()(统一处理同步 / 异步函数为 Promise)、Promise.withResolvers()(简化 Promise 实例创建)等新增方法。
三、Promise 的核心优势
- 解决回调地狱传统回调函数处理多层异步会形成嵌套地狱,代码可读性极差:
javascript
运行
getUser((user) => {
getOrder(user.id, (order) => {
getProduct(order.productId, (product) => {
console.log(product);
}, (err) => { console.error(err); });
}, (err) => { console.error(err); });
}, (err) => { console.error(err); });
而 Promise 链式调用可将嵌套转为线性结构:
javascript
运行
getUser()
.then(user => getOrder(user.id))
.then(order => getProduct(order.productId))
.then(product => console.log(product))
.catch(err => console.error(err));
- 集中处理错误Promise 链中任意环节的错误都可通过末尾的
catch统一捕获,无需为每个异步操作单独处理错误。 - 统一异步状态管理固定的三状态机制避免了异步操作的状态混乱,让异步逻辑更可控。
- 支持异步并行通过
Promise.all等方法可高效处理多个并行异步任务,提升执行效率。 - 统一同步 / 异步逻辑可通过
Promise.try()等方法将同步函数转为 Promise 实例,实现同步与异步代码的统一处理。
四、async/await:Promise 的语法糖
1. 基本介绍
async/await 是 ES7 推出的语法糖,基于 Promise 实现,用类同步的写法处理异步逻辑,大幅提升代码可读性。
- async:修饰函数时,函数返回值会自动封装为 Promise 实例,函数内抛出错误等价于返回
Promise.reject() - await:只能在 async 函数内使用,会暂停函数执行,直到后面的 Promise 状态变为 fulfilled 并返回结果;若 Promise 失败则会抛出错误,需用
try/catch捕获
2. 用法示例
对比 Promise 链式调用和 async/await 写法:
javascript
运行
// Promise 链式调用
getUser()
.then(user => getOrder(user.id))
.then(order => getProduct(order.productId))
.then(product => console.log(product))
.catch(err => console.error(err));
// async/await 写法
async function getProductInfo() {
try {
const user = await getUser();
const order = await getOrder(user.id);
const product = await getProduct(order.productId);
console.log(product);
} catch (err) {
console.error(err);
}
}
getProductInfo();
async 函数的返回值特性示例:
javascript
运行
async function errorFunc() {
throw new Error("出错了"); // 等价于 return Promise.reject(new Error("出错了"))
}
async function okFunc() {
return 'yes' // 等价于 return Promise.resolve('yes')
}
errorFunc().catch(err => console.log(err.message)); // 出错了
okFunc().then(result => console.log(result)); // yes
五、总结
Promise 作为 ES6 异步编程标准,解决了传统回调函数的诸多痛点;而 async/await 则进一步优化了异步代码的写法,让异步逻辑更贴近开发者熟悉的同步思维。