JavaScript异步编程:外卖订单实战讲解
让我们通过一个生动的外卖订单例子来理解JavaScript异步编程的核心概念。
1. 同步 vs 异步:直观对比
同步(排队点餐)
// 同步方式 - 一个人点完下一个人才能点
function synchronousOrder() {
console.log("1. 顾客A开始点餐");
// 模拟点餐耗时
for(let i = 0; i < 1000000000; i++) {} // 阻塞操作
console.log("2. 顾客A点餐完成");
console.log("3. 顾客B开始点餐");
for(let i = 0; i < 1000000000; i++) {} // 再次阻塞
console.log("4. 顾客B点餐完成");
console.log("5. 顾客C开始点餐");
for(let i = 0; i < 1000000000; i++) {} // 再次阻塞
console.log("6. 顾客C点餐完成");
}
synchronousOrder();
// 结果:必须按顺序完成,顾客B和C只能等待
异步(外卖平台接单)
// 异步方式 - 平台可以同时处理多个订单
console.log("1. 平台:开始接受新订单");
setTimeout(() => {
console.log("2. 订单A:商家已接单");
}, 1000);
console.log("3. 平台:继续接受其他订单");
setTimeout(() => {
console.log("4. 订单B:商家已接单");
}, 800);
console.log("5. 平台:订单处理中...");
// 结果:订单不会互相阻塞,可以并行处理
2. 完整外卖订单流程示例
// 模拟一个完整的外卖订单流程
class FoodDelivery {
constructor(orderId, customerName, items) {
this.orderId = orderId;
this.customerName = customerName;
this.items = items;
this.status = "pending";
}
// 模拟异步操作:提交订单
placeOrder() {
return new Promise((resolve, reject) => {
console.log(`🛒 [${this.orderId}] ${this.customerName}提交订单: ${this.items.join(', ')}`);
setTimeout(() => {
const success = Math.random() > 0.1; // 90%成功率
if (success) {
this.status = "order_placed";
console.log(`✅ [${this.orderId}] 订单提交成功`);
resolve(this);
} else {
console.log(`❌ [${this.orderId}] 订单提交失败`);
reject("支付失败");
}
}, 1000);
});
}
// 模拟异步操作:商家准备
prepareFood() {
return new Promise((resolve) => {
console.log(`👨🍳 [${this.orderId}] 商家开始制作...`);
// 模拟制作时间(不同菜品时间不同)
const prepTime = this.items.length * 500 + Math.random() * 1000;
setTimeout(() => {
this.status = "food_ready";
console.log(`🍱 [${this.orderId}] 菜品制作完成 (耗时: ${prepTime.toFixed(0)}ms)`);
resolve(this);
}, prepTime);
});
}
// 模拟异步操作:骑手取餐
pickupOrder() {
return new Promise((resolve) => {
console.log(`🛵 [${this.orderId}] 骑手前往商家...`);
setTimeout(() => {
this.status = "picked_up";
console.log(`📦 [${this.orderId}] 骑手已取餐`);
resolve(this);
}, 1500);
});
}
// 模拟异步操作:配送
deliverOrder() {
return new Promise((resolve) => {
console.log(`🚚 [${this.orderId}] 骑手配送中...`);
// 模拟配送时间(受交通影响)
const deliveryTime = 2000 + Math.random() * 2000;
setTimeout(() => {
this.status = "delivered";
console.log(`🎉 [${this.orderId}] 订单已送达!`);
resolve(this);
}, deliveryTime);
});
}
}
// 使用Promise处理单个订单
function processSingleOrder(order) {
console.log("\n======= 处理单个订单 =======");
order.placeOrder()
.then(order => order.prepareFood())
.then(order => order.pickupOrder())
.then(order => order.deliverOrder())
.then(order => {
console.log(`💝 [${order.orderId}] 订单完成,状态: ${order.status}`);
})
.catch(error => {
console.log(`⚠️ 订单出错: ${error}`);
});
}
// 使用async/await处理订单(更优雅的方式)
async function processOrderAsync(order) {
try {
console.log("\n======= 使用async/await处理订单 =======");
await order.placeOrder();
await order.prepareFood();
await order.pickupOrder();
await order.deliverOrder();
console.log(`💝 [${order.orderId}] 订单完成,状态: ${order.status}`);
return order;
} catch (error) {
console.log(`⚠️ 订单出错: ${error}`);
throw error;
}
}
// 处理多个并发订单
async function processMultipleOrders() {
console.log("\n======= 处理多个并发订单 =======");
// 创建多个订单
const orders = [
new FoodDelivery("ORDER001", "张三", ["红烧肉", "米饭", "可乐"]),
new FoodDelivery("ORDER002", "李四", ["披萨", "沙拉"]),
new FoodDelivery("ORDER003", "王五", ["汉堡", "薯条", "冰淇淋"])
];
// 同时开始所有订单(并行)
const orderPromises = orders.map(order =>
order.placeOrder()
.then(order => order.prepareFood())
.then(order => order.pickupOrder())
.then(order => order.deliverOrder())
.catch(error => {
console.log(`订单${order.orderId}失败: ${error}`);
return null;
})
);
// 等待所有订单完成
const results = await Promise.allSettled(orderPromises);
console.log("\n======= 所有订单处理完成 =======");
const successfulOrders = results.filter(result => result.status === 'fulfilled' && result.value);
console.log(`成功送达: ${successfulOrders.length} 个订单`);
}
// 执行示例
const testOrder = new FoodDelivery("TEST001", "测试用户", ["宫保鸡丁", "蛋炒饭"]);
// 方法1: 使用Promise链式调用
processSingleOrder(testOrder);
// 方法2: 使用async/await(需要稍后执行避免时间重叠)
setTimeout(async () => {
const order2 = new FoodDelivery("TEST002", "async用户", ["牛肉面", "小菜"]);
await processOrderAsync(order2);
// 方法3: 处理多个并发订单
await processMultipleOrders();
}, 5000);
3. 关键异步模式对比
// 1. 回调地狱(Callback Hell) - 不推荐
function callbackHellExample() {
placeOrder((order) => {
prepareFood(order, (food) => {
pickupOrder(food, (delivery) => {
deliverOrder(delivery, (result) => {
console.log("送达!");
// 如果还有更多步骤...代码会向右无限延伸
});
});
});
});
}
// 2. Promise链式调用 - 改进版
function promiseChainExample() {
placeOrder()
.then(order => prepareFood(order))
.then(food => pickupOrder(food))
.then(delivery => deliverOrder(delivery))
.then(result => console.log("送达!"))
.catch(error => console.error("出错:", error));
}
// 3. async/await - 现代推荐
async function asyncAwaitExample() {
try {
const order = await placeOrder();
const food = await prepareFood(order);
const delivery = await pickupOrder(food);
const result = await deliverOrder(delivery);
console.log("送达!");
} catch (error) {
console.error("出错:", error);
}
}
// 4. 并行处理多个异步操作
async function parallelProcessing() {
// 同时开始多个独立操作
const [userInfo, restaurantInfo, promoInfo] = await Promise.all([
getUserInfo(),
getRestaurantInfo(),
getPromotionInfo()
]);
console.log("所有信息已获取,可以下单");
}
// 5. 竞速模式:哪个先完成用哪个
async function raceExample() {
// 从多个骑手中选择最先接单的
const fastestRider = await Promise.race([
findRider("平台A"),
findRider("平台B"),
findRider("平台C")
]);
console.log(`最快的骑手: ${fastestRider.name}`);
}
4. 实际应用示例:订单状态跟踪
// 外卖订单状态实时跟踪系统
class OrderTracker {
constructor() {
this.orders = new Map();
this.statusCallbacks = new Map();
}
// 创建订单并跟踪
async createAndTrackOrder(customer, items) {
const orderId = `ORDER_${Date.now()}`;
const order = new FoodDelivery(orderId, customer, items);
this.orders.set(orderId, order);
// 开始跟踪订单状态
this.trackOrder(orderId);
return orderId;
}
// 跟踪订单状态变化
async trackOrder(orderId) {
const order = this.orders.get(orderId);
if (!order) return;
const statusCheck = setInterval(async () => {
const previousStatus = order.status;
// 模拟状态更新(实际中可能来自WebSocket或API轮询)
const statuses = ["order_placed", "food_ready", "picked_up", "delivered"];
const currentIndex = statuses.indexOf(previousStatus);
if (currentIndex < statuses.length - 1) {
// 模拟状态前进
if (Math.random() > 0.7) {
order.status = statuses[currentIndex + 1];
console.log(`📱 [${orderId}] 状态更新: ${previousStatus} → ${order.status}`);
// 触发回调
this.triggerCallbacks(orderId, order.status);
// 如果送达,停止跟踪
if (order.status === "delivered") {
clearInterval(statusCheck);
console.log(`📱 [${orderId}] 跟踪结束`);
}
}
}
}, 2000);
}
// 注册状态变化回调
onStatusChange(orderId, callback) {
if (!this.statusCallbacks.has(orderId)) {
this.statusCallbacks.set(orderId, []);
}
this.statusCallbacks.get(orderId).push(callback);
}
// 触发所有回调
triggerCallbacks(orderId, status) {
const callbacks = this.statusCallbacks.get(orderId) || [];
callbacks.forEach(callback => {
try {
callback(status);
} catch (error) {
console.error("回调执行错误:", error);
}
});
}
}
// 使用示例
async function demoOrderTracking() {
console.log("\n======= 订单实时跟踪演示 =======");
const tracker = new OrderTracker();
// 创建订单
const orderId = await tracker.createAndTrackOrder("跟踪用户", ["麻辣香锅", "米饭"]);
// 注册状态变化监听
tracker.onStatusChange(orderId, (status) => {
const statusMessages = {
"order_placed": "商家已接单",
"food_ready": "菜品已制作完成",
"picked_up": "骑手已取餐",
"delivered": "已送达"
};
console.log(`📲 通知: 订单${orderId} ${statusMessages[status] || status}`);
// 可以在这里更新UI、发送通知等
if (status === "delivered") {
console.log("🎊 订单完成!请给骑手评价");
}
});
console.log(`开始跟踪订单: ${orderId}`);
console.log("等待状态更新...\n");
}
// 运行演示
demoOrderTracking();
5. 关键概念总结
| 概念 |
外卖订单比喻 |
JavaScript实现 |
|---|
| 同步 |
一个订单完全处理完才处理下一个 |
阻塞操作,代码顺序执行 |
| 异步 |
同时处理多个订单,不互相等待 |
非阻塞,使用回调/Promise/async-await |
| 回调函数 |
订单状态更新时打电话通知你 |
function callback(error, result) {} |
| Promise |
订单承诺:一定会送达或通知失败 |
new Promise((resolve, reject) => {}) |
| async/await |
"等待"订单完成再继续下一步 |
async function() { await task(); } |
| 并行处理 |
多个餐厅同时准备不同菜品 |
Promise.all([task1, task2, task3]) |
| 竞速模式 |
多个骑手抢单,第一个接单的配送 |
Promise.race([rider1, rider2]) |
6. 练习建议
修改示例代码,添加订单取消功能
实现一个超时机制:如果30分钟未送达自动取消
创建订单队列系统,控制同时处理的订单数量
添加重试机制:骑手取消后自动重新分配
通过这个外卖订单的例子,你可以直观地理解JavaScript异步编程的核心概念。异步编程的本质就是"不等待",让程序在等待一个操作完成时,可以继续处理其他任务,就像外卖平台可以同时处理无数订单一样。