在现代Web开发中,异步操作已经成为不可避免的一部分。无论是从服务器获取数据,还是处理用户的输入,我们都需要有效地管理这些异步任务。然而,传统的回调函数(Callback)方式容易导致代码变得复杂难懂,形成所谓的“回调地狱”(Callback Hell)。在这样的背景下,JavaScript引入了Promise这一强大的工具,帮助开发者更优雅地处理异步操作。然而promise又是什么呢,我们为什么需要使用promise呢。

JavaScript 为什么需要 Promise
JavaScript是一种单线程语言,这意味着它一次只能执行一个任务。然而,现代Web应用需要处理大量的异步操作,如网络请求、定时器、文件读取等。如果所有这些操作都在主线程上同步进行,用户界面将会被阻塞(页面卡死),用户体验将非常差。因此,JavaScript需要一种机制来处理这些异步操作,使得主线程可以继续处理其他任务。
Promise 解决了什么问题
回调地狱(Callback Hell) 在Promise之前,处理异步操作主要依赖回调函数。然而,当多个异步操作需要依赖前一个操作的结果时,回调函数会相互嵌套,代码变得难以阅读和维护,这种现象被称为回调地狱。例如:
doSomething(function(result1) {
doSomethingElse(result1, function(result2) {
doAnotherThing(result2, function(result3) {
doSomethingMore(result3, function(result4) {
// 继续嵌套回调
});
});
});
});
这种嵌套结构不仅难以阅读,而且难以调试和维护。 更好的错误处理 在使用回调函数处理异步操作时,错误处理通常也需要通过回调函数来进行,这使得错误处理逻辑变得分散和混乱:
doSomething(function(error, result1) {
if (error) {
// 处理错误
} else {
doSomethingElse(result1, function(error, result2) {
if (error) {
// 处理错误
} else {
doAnotherThing(result2, function(error, result3) {
if (error) {
// 处理错误
} else {
// 继续处理
}
});
}
});
}
});
这种方式不仅代码冗长,而且容易遗漏错误处理的情况。
场景案例
比如现在有一个电商平台项目的操作场景:
用户登录-查看订单-查看订单的商品信息。这里的每个步骤都是异步操作,并且需要等待前一个步骤完成后才能进行下一个步骤。比如,我们需要依次完成以下任务:
- 获取用户数据
- 根据用户数据获取订单信息
- 根据订单信息获取商品详情
- 显示最终结果
使用传统的回调函数来处理这些异步操作时,代码可能会变成这样:
function getUserData(callback) {
setTimeout(() => {
console.log("获取用户数据");
callback(null, { userId: 1 });
}, 1000);
}
function getOrderData(userId, callback) {
setTimeout(() => {
console.log("获取订单数据");
callback(null, { orderId: 101 });
}, 1000);
}
function getProductData(orderId, callback) {
setTimeout(() => {
console.log("获取商品详情");
callback(null, { productId: 1001, productName: "Laptop" });
}, 1000);
}
function displayResult(product) {
console.log("显示结果: ", product);
}
getUserData((error, user) => {
if (error) {
console.error("获取用户数据出错: ", error);
} else {
getOrderData(user.userId, (error, order) => {
if (error) {
console.error("获取订单数据出错: ", error);
} else {
getProductData(order.orderId, (error, product) => {
if (error) {
console.error("获取商品详情出错: ", error);
} else {
displayResult(product);
}
});
}
});
}
});
那promise 是什么呢
假设你在游戏中领取一个任务完成后才能获取奖励。这是一个异步操作,因为你需要完成任务才能获取对应的奖励。Promise在这里就像是你的一个承诺(promise),它有三种可能的状态:
- Pending(进行中):任务进行中。
- Fulfilled(已完成):任务完成获取奖励。
- Rejected(已失败):任务失败。
Promise 是一种用于处理异步操作的对象。它代表了一个在未来可能完成或者失败的操作,并且可以用来处理操作的结果。Promise有三种状态:Pending(进行中)、Fulfilled(已成功)和Rejected(已失败)。
创建一个promise,promise构造函数接受一个执行函数作为参数,该执行函数有两个参数:resolve和reject。这两个参数也是函数,用于在异步操作成功或失败时分别处理结果。
const promise = new Promise((resolve, reject) => {
// 异步操作
if (操作成功) {
resolve(结果值); // 操作成功,传递结果值
} else {
reject(错误原因); // 操作失败,传递错误原因
}
});
function isInclude(item,list){
return new Promise(
(resolve,reject)=>{
list.includes(item) ? resolve(true) : reject(false)
}
)
}
promise实例上的方法
这些方法都需要传递一个回调函数,回调函数中执行处理不同结果的逻辑。
- .then(): 处理成功的结果。
promise.then(value => {
// 处理成功的结果
});
这里的value接收到的就是调用resolve传递值。
- .catch(): 处理错误。
promise.catch(error => {
// 处理错误
});
这里的error接收到的就是调用reject传递的错误信息。
- .finally(): 无论成功或失败都会执行。
promise.finally(() => {
// 最终执行的代码
});
finally的回调没有参数。
finally一般是在收尾的任务中,比如在页面打开时请求对应的数据进行页面渲染,在数据请求的过程中页面会有一个loading状态(页面加载一直转圈圈)
在这个情况下不管请求成功还是失败都应该取消这个状态,这个时候就需要finally收尾。
promise链式调用
链式调用就是将promise的方法调用多次,上一次方法的执行结果作为下一次方法的入参。
promise
.then(value => {
// 处理成功的结果
return nextValue;
})
.then(nextValue => {
// 处理下一个结果
})
.catch(error => {
// 处理错误
});

promise
.then(value => {
// 处理成功的结果
return nextValue;
})
.catch(error => {
// 处理错误
});
const myPromise = new Promise((resolve, reject) => {
// 模拟异步操作
setTimeout(() => {
resolve("Success");
}, 1000);
});
myPromise
.then((result) => {
console.log(result); // 输出: Success
// 在 then 方法中抛出一个错误
throw new Error("Something went wrong!");
})
.catch((error) => {
// 捕获并处理错误
console.error(error.message); // 输出: Something went wrong!
});
const myPromise = new Promise((resolve, reject) => {
// 模拟异步操作
setTimeout(() => {
reject(new Error("Initial error"));
}, 1000);
});
myPromise
.then((result) => {
console.log("This will not be called");
return result;
})
.catch((error) => {
console.error("Caught an error:", error.message); // 输出: Initial error
// 返回一个新的值,继续后续的 then 方法
return "Recovered from error";
})
.then((result) => {
console.log("Continuing with:", result); // 输出: Recovered from error
})
.catch((error) => {
// 捕获任何在前面的 then 方法中抛出的错误
console.error("Caught another error:", error.message);
});
1、每一个then方法还是catch方法return的值都会被包裹为一个promise对象 2、上一个方法返回的值会作为下一个方法的参数 3、then方法可以通过throw抛出异常给catch处理,catch可以继续return将返回的值给then进行处理 4、每一个catch方法只会捕获自己前面发生的错误,捕获后面的catch并不会继续捕获。
为什么promise可以实现异步
Promise 并不直接实现异步操作,而是用于管理和协调异步操作的结果。JavaScript 本身提供了异步操作的机制,例如通过事件循环(Event Loop)和任务队列(Task Queue)来实现异步行为。Promise 是一种抽象,提供了一种更简洁和可读的方式来处理这些异步操作。
JavaScript 的异步机制
JavaScript 是单线程的,这意味着它一次只能执行一个任务。但是,为了避免在等待耗时操作(如网络请求、文件读取)时阻塞主线程,JavaScript 引入了事件循环和任务队列来管理异步任务。
- 事件循环(Event Loop):事件循环不断检查调用栈(Call Stack)和任务队列(Task Queue)。如果调用栈为空,它会从任务队列中取出一个任务并将其推入调用栈执行。
- 任务队列(Task Queue):异步操作(如setTimeout、网络请求)的回调函数会被放入任务队列中,等待事件循环将它们推入调用栈执行。
promise的运用
Promise在JavaScript中被广泛用于处理各种异步操作,特别是在处理网络请求、文件操作、定时器等场景中。下面我们通过几个实际应用的案例来展示Promise的运用。
在之前我们举了一个关于回调地域的例子,当我们使用promise来实现网络请求是什么样子呢
使用Promise可以避免回调地狱,使代码更加清晰和易于维护:
function getUserData() {
return new Promise((resolve, reject) => {
setTimeout(() => {
console.log("获取用户数据");
resolve({ userId: 1 });
}, 1000);
});
}
function getOrderData(userId) {
return new Promise((resolve, reject) => {
setTimeout(() => {
console.log("获取订单数据");
resolve({ orderId: 101 });
}, 1000);
});
}
function getProductData(orderId) {
return new Promise((resolve, reject) => {
setTimeout(() => {
console.log("获取商品详情");
resolve({ productId: 1001, productName: "Laptop" });
}, 1000);
});
}
function displayResult(product) {
console.log("显示结果: ", product);
}
getUserData()
.then(user => {
return getOrderData(user.userId);
})
.then(order => {
return getProductData(order.orderId);
})
.then(product => {
displayResult(product);
})
.catch(error => {
console.error("出错了: ", error);
});
案例1:处理网络请求
假设我们需要从一个API获取用户信息,然后根据用户信息获取该用户的订单信息,最后获取每个订单的详细信息。
// 模拟一个异步请求:获取用户信息
function fetchUser() {
return new Promise((resolve, reject) => {
setTimeout(() => {
console.log("用户信息获取成功");
resolve({ userId: 1, userName: "John Doe" });
}, 1000);
});
}
// 模拟一个异步请求:获取订单信息
function fetchOrders(userId) {
return new Promise((resolve, reject) => {
setTimeout(() => {
console.log("订单信息获取成功");
resolve([{ orderId: 101 }, { orderId: 102 }]);
}, 1000);
});
}
// 模拟一个异步请求:获取订单详情
function fetchOrderDetails(orderId) {
return new Promise((resolve, reject) => {
setTimeout(() => {
console.log(`订单${orderId}详情获取成功`);
resolve({ orderId: orderId, product: "Laptop", price: 1000 });
}, 1000);
});
}
// 使用Promise处理这些异步请求
fetchUser()
.then(user => {
console.log("用户信息: ", user);
return fetchOrders(user.userId);
})
.then(orders => {
console.log("订单信息: ", orders);
return Promise.all(orders.map(order => fetchOrderDetails(order.orderId)));
})
.then(orderDetails => {
console.log("所有订单详情: ", orderDetails);
})
.catch(error => {
console.error("出错了: ", error);
});
案例2:顺序执行多个异步操作
有时候我们需要按顺序执行多个异步操作,并且前一个操作的结果会影响后一个操作的输入。我们可以通过Promise链式调用来实现这一点。
// 模拟一个异步操作:初始化应用
function initializeApp() {
return new Promise((resolve, reject) => {
setTimeout(() => {
console.log("应用初始化成功");
resolve("初始化数据");
}, 1000);
});
}
// 模拟一个异步操作:用户登录
function userLogin(initData) {
return new Promise((resolve, reject) => {
setTimeout(() => {
console.log("用户登录成功");
resolve({ userId: 1, token: "abc123" });
}, 1000);
});
}
// 模拟一个异步操作:获取用户配置
function fetchUserSettings(user) {
return new Promise((resolve, reject) => {
setTimeout(() => {
console.log("用户配置获取成功");
resolve({ theme: "dark", language: "en" });
}, 1000);
});
}
// 使用Promise链式调用按顺序执行这些异步操作
initializeApp()
.then(initData => {
return userLogin(initData);
})
.then(user => {
return fetchUserSettings(user);
})
.then(settings => {
console.log("最终用户配置: ", settings);
})
.catch(error => {
console.error("出错了: ", error);
});
Promise.all
Promise.all 是JavaScript中的一个静态方法,用于处理多个Promise对象。当所有的Promise都成功时,Promise.all 返回一个新的Promise,其状态为Fulfilled,并携带一个包含所有Promise成功结果的数组;如果任何一个Promise失败,Promise.all 立即返回一个Rejected的Promise,并携带第一个失败的Promise的错误信息。
使用场景
Promise.all 通常用于需要并行执行多个异步操作,并且所有操作都完成后再进行下一步处理的情况。例如,从多个API获取数据(并发请求),读取多个文件,或执行多个独立的异步任务。
语法
Promise.all(iterable);
- iterable 是一个可迭代对象(通常是一个数组),包含多个Promise对象。
示例
假设我们需要同时获取天气信息、新闻信息和股票信息,并在所有数据都获取完成后进行处理。
// 模拟异步操作:获取天气信息
function fetchWeather() {
return new Promise((resolve) => {
setTimeout(() => {
resolve({ weather: "Sunny", temperature: 30 });
}, 1000);
});
}
// 模拟异步操作:获取新闻信息
function fetchNews() {
return new Promise((resolve) => {
setTimeout(() => {
resolve(["新闻1", "新闻2", "新闻3"]);
}, 2000);
});
}
// 模拟异步操作:获取股票信息
function fetchStocks() {
return new Promise((resolve) => {
setTimeout(() => {
resolve({ stock: "AAPL", price: 150 });
}, 1500);
});
}
// 使用Promise.all并行处理这些异步请求
Promise.all([fetchWeather(), fetchNews(), fetchStocks()])
.then((results) => {
const [weather, news, stocks] = results;
console.log("天气信息: ", weather);
console.log("新闻信息: ", news);
console.log("股票信息: ", stocks);
})
.catch((error) => {
console.error("出错了: ", error);
});