异步编程是一种编程模式,允许代码在等待某些操作完成的同时继续执行其他任务,不会阻塞主线程。
同步与异步的区别:
- 同步:按顺序执行,前一个未完成,后一个无法开始;
- 异步:代码可以并行。
一、异步编程原理
执行模型:
- 单线程:只有一个主线程,负责执行代码;
- 事件循环:管理任务队列的执行机制;
- 任务队列:宏任务队列和微任务队列。

异步编程作用:
- 避免阻塞ui:防止长时间运行冻结页面;
- 提高性能;
- 改善用户体验;
- 处理i/o密集型操作:网络请求,文件读写,数据库操作等。
二、异步编程的实现方法
1、回调函数
回调函数是作为参数传递给另一个函数的函数,在特定条件满足或事件发生时被调用。

(1)回调函数的本质
回调函数体现了函数可以:
- 被赋值给变量
- 作为参数传递
- 作为返回值
- 存储在数据结构中

(2)回调函数的类型
a、同步回调
javascript
// 同步回调示例
function processArray(arr, processor) {
const result = [];
for (let i = 0; i < arr.length; i++) {
// 同步执行回调
result.push(processor(arr[i]));
}
return result;
}
const numbers = [1, 2, 3, 4, 5];
const doubled = processArray(numbers, (num) => num * 2);
console.log(doubled); // [2, 4, 6, 8, 10]
// JavaScript内置的同步回调方法
const fruits = ['apple', 'banana', 'cherry'];
fruits.forEach((fruit, index) => {
console.log(`${index}: ${fruit}`);
});
// 输出:
// 0: apple
// 1: banana
// 2: cherry
b、异步回调
javascript
// 异步回调示例
function fetchData(url, onSuccess, onError) {
console.log(`开始从 ${url} 获取数据...`);
// 模拟异步操作
setTimeout(() => {
const success = Math.random() > 0.3;
if (success) {
const data = { url, timestamp: Date.now() };
onSuccess(data); // 成功回调
} else {
onError(new Error(`从 ${url} 获取数据失败`)); // 错误回调
}
}, 1000);
}
// 使用异步回调
fetchData(
'https://api.example.com/users',
(data) => {
console.log('数据获取成功:', data);
},
(error) => {
console.error('发生错误:', error.message);
}
);
(3)回调函数的部分应用示例
数组中的回调;
javascript
const numbers = [1, 2, 3, 4, 5];
// map: 转换数组
const squared = numbers.map((num) => num * num);
console.log('平方:', squared); // [1, 4, 9, 16, 25]
// filter: 过滤数组
const evenNumbers = numbers.filter((num) => num % 2 === 0);
console.log('偶数:', evenNumbers); // [2, 4]
// reduce: 累积计算
const sum = numbers.reduce((accumulator, current) => {
return accumulator + current;
}, 0);
console.log('总和:', sum); // 15
// sort: 排序
const unsorted = [3, 1, 4, 1, 5, 9, 2];
const sorted = unsorted.sort((a, b) => a - b);
console.log('排序后:', sorted); // [1, 1, 2, 3, 4, 5, 9]
// find: 查找元素
const firstEven = numbers.find((num) => num % 2 === 0);
console.log('第一个偶数:', firstEven); // 2
事件处理中的回调:
javascript
// DOM事件回调
document.addEventListener('DOMContentLoaded', () => {
console.log('DOM已加载完成');
const button = document.getElementById('myButton');
if (button) {
// 添加点击事件回调
button.addEventListener('click', (event) => {
console.log('按钮被点击', event);
alert('按钮被点击!');
});
// 添加鼠标悬停事件回调
button.addEventListener('mouseenter', () => {
button.style.backgroundColor = '#f0f0f0';
});
button.addEventListener('mouseleave', () => {
button.style.backgroundColor = '';
});
}
});
// 创建自定义事件处理器
class EventHandler {
constructor() {
this.events = {};
}
// 注册事件回调
on(eventName, callback) {
if (!this.events[eventName]) {
this.events[eventName] = [];
}
this.events[eventName].push(callback);
}
// 触发事件,执行所有回调
emit(eventName, data) {
if (this.events[eventName]) {
this.events[eventName].forEach(callback => {
try {
callback(data);
} catch (error) {
console.error(`事件 ${eventName} 的回调执行失败:`, error);
}
});
}
}
// 移除事件回调
off(eventName, callbackToRemove) {
if (this.events[eventName]) {
this.events[eventName] = this.events[eventName].filter(
callback => callback !== callbackToRemove
);
}
}
}
// 使用自定义事件处理器
const handler = new EventHandler();
const userLoggedIn = (user) => {
console.log(`用户 ${user.name} 已登录`);
};
handler.on('login', userLoggedIn);
handler.on('login', (user) => {
console.log(`发送登录通知给 ${user.email}`);
});
handler.emit('login', { name: '张三', email: 'zhangsan@example.com' });
// 移除特定回调
handler.off('login', userLoggedIn);
实现防抖和节流:
javascript
// 防抖:在事件频繁触发时,只执行最后一次
function debounce(fn, delay) {
let timer = null;
return function(...args) {
// 清除之前的定时器
clearTimeout(timer);
// 设置新的定时器
timer = setTimeout(() => {
fn.apply(this, args);
}, delay);
};
}
// 节流:在事件频繁触发时,每隔一段时间执行一次
function throttle(fn, interval) {
let lastTime = 0;
let timer = null;
return function(...args) {
const now = Date.now();
const remainingTime = interval - (now - lastTime);
if (remainingTime <= 0) {
// 距离上次执行已经超过间隔时间
lastTime = now;
fn.apply(this, args);
} else if (!timer) {
// 设置定时器,确保最后一次也会执行
timer = setTimeout(() => {
lastTime = Date.now();
timer = null;
fn.apply(this, args);
}, remainingTime);
}
};
}
// 使用示例
const searchInput = document.getElementById('search');
const logMessage = debounce((message) => {
console.log('搜索关键词:', message);
// 这里可以执行实际的搜索逻辑
}, 300);
searchInput.addEventListener('input', (event) => {
logMessage(event.target.value);
});
// 窗口滚动节流
const handleScroll = throttle(() => {
console.log('滚动位置:', window.scrollY);
// 这里可以执行滚动相关的逻辑
}, 200);
window.addEventListener('scroll', handleScroll);
(4)回调地狱
指多层嵌套难以阅读和维护。
javascript
// 典型的回调地狱示例
getUser(1, (error, user) => {
if (error) {
console.error('获取用户失败:', error);
} else {
getOrders(user.id, (error, orders) => {
if (error) {
console.error('获取订单失败:', error);
} else {
getOrderDetails(orders[0].id, (error, details) => {
if (error) {
console.error('获取订单详情失败:', error);
} else {
updateInventory(details.productId, (error, result) => {
if (error) {
console.error('更新库存失败:', error);
} else {
console.log('所有操作完成');
}
});
}
});
}
});
}
});
解决方案:
- 命名为函数(减少嵌套);
- 使用async/await;
- 使用promise链;
2、Promise
promise是JavaScript中处理异步操作的对象,他代表一个异步操作的最终完成或失败及其结果值。(举个例子:当你发起一个异步任务,JavaScript会给你一个promise对象,此时这个对象就像一个空盒子/提货单,他代表一个承诺未来的某个时刻,这个盒子中装着你想要的结果,也可能是一个失败的原因)。
特点:
- 管理异步:专门用来处理网络请求,定时器,读写文件等;
- 避免回调地狱:promise提供链式调用的方式根据promise提供的三种状态去书写异步逻辑;
- 统一处理错误:通过.catch()可以在链式调用的末尾统一捕获和处理整个链条任何错误;
- 协调多个异步任务:promise.all等所有任务都成功,promis.race只看谁最快完成。

(1)promise三状态
- Pending(待定状态):初始状态,及没有被兑现也没有被拒绝;
- Fulfilled(已兑现状态):操作成功完成;
- Rejected(已拒绝状态):操作失败。

(2)promise工作原理
promise使用微任务队列,优先级高于宏任务。(就是说在宏任务(主进程)运行结束后每次都会先去检查微任务列表是否为空,若不为空,则优先响应微任务,直到其空为止才去执行下一个宏任务;在执行宏任务期间若是来了新的微任务则不会响应,只有在宏任务完成之后才会响应)。
javascript
console.log('1. 开始');
setTimeout(() => {
console.log('6. setTimeout (宏任务)');
}, 0);
Promise.resolve()
.then(() => {
console.log('3. Promise 1 (微任务)');
return '结果';
})
.then((result) => {
console.log('4. Promise 2:', result);
});
Promise.resolve().then(() => {
console.log('5. Promise 3 (微任务)');
});
console.log('2. 结束');
// 输出顺序:
// 1. 开始
// 2. 结束
// 3. Promise 1 (微任务)
// 5. Promise 3 (微任务)
// 4. Promise 2: 结果
// 6. setTimeout (宏任务)
4在5之后的原因:微任务队列是先进先出,由于两个promise是同步代码执行,而4是第一个promise执行完之后产生的新任务,所以他将会被追加到队列末尾。
口诀:同步先,微次之,宏最后;微队列,顺序走,新任务,排后头。
javascript
function demonstratePromiseFlow() {
console.log('1. 创建 Promise');
const promise = new Promise((resolve, reject) => {
console.log('2. 执行器函数立即执行');
setTimeout(() => {
console.log('4. 异步操作完成');
resolve('最终结果');
}, 100);
});
console.log('3. Promise 已创建,状态为 pending');
promise.then((result) => {
console.log('5. then() 被调用:', result);
});
console.log('6. 同步代码继续执行');
}
demonstratePromiseFlow();
//1. 创建 Promise
//2. 执行器函数立即执行
//3. Promise 已创建,状态为 pending
//6. 同步代码继续执行
//4. 异步操作完成
//5. then() 被调用:最终结果
注:.then()回调不会在注册时立即执行,而是在promise被resolve后才会执行
所以在1、2执行完,setTimeout被调用到宏任务队列100ms后执行;随后执行3宏任务,遇到4时由于当前promise是pending状态所以回调被保存起来,执行6宏任务结束后执行settimeout随后promise状态变为fulfilled触发5.
(3)promise核心方法
a、构造函数
javascript
// 创建 Promise
const promise = new Promise((resolve, reject) => {
// 这个函数被称为 "执行器函数" (executor)
// 它立即执行
// 模拟异步操作
const operation = () => {
try {
const result = doSomeWork();
resolve(result); // 成功时调用
} catch (error) {
reject(error); // 失败时调用
}
};
setTimeout(operation, 1000);
});
// 执行器函数的执行时机
console.log('1. 创建 Promise 前');
const promise2 = new Promise(() => {
console.log('2. 执行器函数立即执行');
});
console.log('3. Promise 已创建');
b、then()方法
javascript
// then() 的基本用法
promise.then(
// 第一个参数:onFulfilled,成功时的回调
(value) => {
console.log('成功:', value);
return value + ' 已处理';
},
// 第二个参数:onRejected,失败时的回调(可选)
(error) => {
console.error('失败:', error);
throw new Error('处理失败');
}
);
// then() 返回新的 Promise
const promiseChain = Promise.resolve(10)
.then(value => {
console.log('第一步:', value);
return value * 2; // 返回值会成为下一个 then 的参数
})
.then(value => {
console.log('第二步:', value);
return value + 5;
})
.then(value => {
console.log('第三步:', value);
return value;
});
promiseChain.then(finalValue => {
console.log('最终结果:', finalValue);
});
// 输出:第一步: 10, 第二步: 20, 第三步: 25, 最终结果: 25
c、catch()方法
javascript
// catch() 用于错误处理
Promise.reject(new Error('出错了!'))
.then(value => {
console.log('这个不会执行');
return value;
})
.catch(error => {
console.error('捕获到错误:', error.message);
return '默认值';
})
.then(value => {
console.log('继续执行:', value);
});
// 多个 catch() 的链式调用
Promise.resolve()
.then(() => {
throw new Error('错误1');
})
.catch(error => {
console.log('捕获错误1:', error.message);
throw new Error('错误2');
})
.catch(error => {
console.log('捕获错误2:', error.message);
return '恢复执行';
})
.then(value => {
console.log('最终:', value);
});
d、finally()方法
javascript
// finally() 无论成功失败都会执行
function processData(data) {
console.log('开始处理数据...');
return new Promise((resolve, reject) => {
setTimeout(() => {
const success = Math.random() > 0.5;
if (success) {
resolve(`处理成功: ${data}`);
} else {
reject(new Error(`处理失败: ${data}`));
}
}, 1000);
})
.finally(() => {
console.log('清理工作完成');
});
}
// 使用 finally
processData('测试数据')
.then(result => console.log('结果:', result))
.catch(error => console.error('错误:', error.message));
e、静态方法------Promise.resolve()
javascript
// 创建已解决的 Promise
const resolvedPromise = Promise.resolve('立即值');
// 等价于
const equivalentPromise = new Promise(resolve => {
resolve('立即值');
});
// 特殊用法:处理 thenable 对象
const thenable = {
then: function(resolve, reject) {
resolve('来自 thenable 的值');
}
};
Promise.resolve(thenable).then(value => {
console.log(value); // '来自 thenable 的值'
});
Thenable是任何具有.then()方法的对象
普通resolve和thenable的resolve区别:
- 普通resolve:直接设置promise的状态和值,立即执行,不会等待;传递什么值promise就有什么值;不涉及任何异步操作。
- thenable的resolve:先调用thenable.then()方法,状态等该方法执行完后才确定;最终值取决于thenable.ehtn()中调用的resolve;如果thenable.then()中有异步操作会等待完成。
thenable的resolve特点:
- Promise.resolve会自动展开thenable
- thenable可以不是真正的Promise(任何拥有then方法的对象都是thenable)
- 嵌套thenable会被递归展开
f、静态方法------Promise.reject()
javascript
// 创建已拒绝的 Promise
const rejectedPromise = Promise.reject(new Error('拒绝原因'));
// 实际应用:快速创建错误响应
function getUser(id) {
if (!id) {
return Promise.reject(new Error('ID 不能为空'));
}
// 正常的异步操作...
}
g、静态方法------Promise.all()
javascript
// 并行执行多个 Promise,所有成功才成功
const promises = [
fetchData('/api/user/1'),
fetchData('/api/user/2'),
fetchData('/api/user/3')
];
Promise.all(promises)
.then(results => {
console.log('所有数据获取成功:', results);
// results: [user1, user2, user3]
})
.catch(error => {
console.error('有一个请求失败:', error);
// 只要有一个失败,整个 Promise.all 就失败
});
// 实际示例
const timeout = (ms, value) =>
new Promise(resolve => setTimeout(() => resolve(value), ms));
Promise.all([
timeout(100, '第一个'),
timeout(200, '第二个'),
timeout(300, '第三个')
]).then(values => {
console.log('所有完成:', values); // 大约300ms后输出
});
h、静态方法------Promise.allSettled()
javascript
// 等待所有 Promise 完成(无论成功或失败)
const promises = [
Promise.resolve('成功1'),
Promise.reject('失败1'),
Promise.resolve('成功2'),
Promise.reject('失败2')
];
Promise.allSettled(promises)
.then(results => {
console.log('所有 Promise 都已处理:');
results.forEach((result, index) => {
if (result.status === 'fulfilled') {
console.log(`Promise ${index}: 成功`, result.value);
} else {
console.log(`Promise ${index}: 失败`, result.reason);
}
});
});
i、静态方法------Promise.race()
javascript
// 竞速:第一个完成的 Promise 决定结果
const timeoutPromise = timeout =>
new Promise((_, reject) =>
setTimeout(() => reject(new Error('超时')), timeout)
);
const fetchPromise = fetchData('/api/data');
Promise.race([fetchPromise, timeoutPromise(5000)])
.then(data => {
console.log('数据获取成功:', data);
})
.catch(error => {
console.error('请求超时或失败:', error.message);
});
// 实际应用:最快响应胜出
const fastestAPI = Promise.race([
fetch('https://api1.example.com/data'),
fetch('https://api2.example.com/data'),
fetch('https://api3.example.com/data')
]);
j、静态方法------Promise.any()
javascript
// 第一个成功的 Promise 决定结果(忽略失败的)
const apis = [
fetch('https://api1.example.com').catch(() => 'api1失败'),
fetch('https://api2.example.com').catch(() => 'api2失败'),
fetch('https://api3.example.com') // 假设这个成功
];
Promise.any(apis)
.then(firstSuccess => {
console.log('第一个成功的响应:', firstSuccess);
})
.catch(error => {
console.error('所有请求都失败了:', error);
// error.aggregateErrors 包含所有失败原因
});
(4)promise简单应用
网络请求处理
javascript
// 使用 Promise 封装 XMLHttpRequest
function httpRequest(method, url, data = null) {
return new Promise((resolve, reject) => {
const xhr = new XMLHttpRequest();
xhr.open(method, url);
xhr.setRequestHeader('Content-Type', 'application/json');
xhr.onload = () => {
if (xhr.status >= 200 && xhr.status < 300) {
try {
const response = JSON.parse(xhr.responseText);
resolve(response);
} catch (error) {
resolve(xhr.responseText);
}
} else {
reject(new Error(`请求失败: ${xhr.status}`));
}
};
xhr.onerror = () => {
reject(new Error('网络错误'));
};
xhr.onabort = () => {
reject(new Error('请求被取消'));
};
xhr.send(data ? JSON.stringify(data) : null);
});
}
// 使用示例
httpRequest('GET', 'https://api.example.com/users')
.then(users => {
console.log('获取用户列表:', users);
return httpRequest('POST', 'https://api.example.com/users', {
name: '新用户'
});
})
.then(newUser => {
console.log('创建新用户成功:', newUser);
})
.catch(error => {
console.error('请求出错:', error.message);
});
异步文件处理
javascript
// 模拟异步文件操作
const fileSystem = {
readFile: (filename) => {
return new Promise((resolve, reject) => {
console.log(`开始读取 ${filename}...`);
setTimeout(() => {
const success = Math.random() > 0.2;
if (success) {
resolve(`文件 ${filename} 的内容`);
} else {
reject(new Error(`读取 ${filename} 失败`));
}
}, Math.random() * 1000);
});
},
writeFile: (filename, content) => {
return new Promise((resolve, reject) => {
console.log(`开始写入 ${filename}...`);
setTimeout(() => {
resolve(`文件 ${filename} 写入成功`);
}, Math.random() * 1000);
});
}
};
// 顺序处理多个文件
async function processFilesSequentially() {
try {
const file1 = await fileSystem.readFile('file1.txt');
console.log('文件1:', file1);
const file2 = await fileSystem.readFile('file2.txt');
console.log('文件2:', file2);
const result = await fileSystem.writeFile('output.txt', file1 + '\n' + file2);
console.log(result);
} catch (error) {
console.error('文件处理失败:', error.message);
}
}
// 并行处理多个文件
function processFilesInParallel() {
const filePromises = [
fileSystem.readFile('file1.txt'),
fileSystem.readFile('file2.txt'),
fileSystem.readFile('file3.txt')
];
return Promise.all(filePromises)
.then(contents => {
console.log('所有文件读取成功:', contents);
const combined = contents.join('\n---\n');
return fileSystem.writeFile('combined.txt', combined);
})
.then(result => {
console.log('合并文件成功:', result);
})
.catch(error => {
console.error('文件操作失败:', error.message);
});
}
3、Async/Await
让基于promise的异步代码看上去更接近于同步代码。
- async:用于声明一个函数,并且该函数总返回一个promise(如果该函数本身返回一个非promise,则async会自动将这个值用Promise.resolve包装成一个成功的promise,如果函数内部出错则会返回一个被拒绝状态的promise);
- await:只能用于async声明的函数内部(await后面总是跟一个promise或thenable对象);用于暂停所在async函数的执行,等待后面promise完成(在等待期间,JavaScript引擎可以离开这个函数去执行其他代码)。
注:在使用async与await时可以用try...catch进行捕获(纯promise不行)
javascript
// 对比:Promise vs async/await
// 使用 Promise
function fetchDataWithPromise() {
return fetch('/api/data')
.then(response => response.json())
.then(data => processData(data))
.catch(error => console.error(error));
}
// 使用 async/await
async function fetchDataWithAsync() {
try {
const response = await fetch('/api/data');
const data = await response.json();
return processData(data);
} catch (error) {
console.error(error);
}
}
(1)async将函数包装成promise

(2)async/await执行流程
javascript
async function demonstration() {
console.log('1. 开始执行');
// await 会暂停函数执行,但不会阻塞主线程
const data = await fetchData(); // 假设返回 Promise
console.log('3. 获取到数据:', data);
return data;
}
console.log('0. 调用 async 函数');
const promise = demonstration();
console.log('2. async 函数立即返回 Promise:', promise);
promise.then(data => console.log('4. 最终结果:', data));
// 输出顺序:
// 0. 调用 async 函数
// 1. 开始执行
// 2. async 函数立即返回 Promise: Promise { <pending> }
// 3. 获取到数据: ...
// 4. 最终结果: ...
(3)核心特性
javascript
//串行执行
// 多个异步操作按顺序执行
async function processUser(userId) {
// 1. 获取用户信息
const user = await getUser(userId);
console.log('用户:', user.name);
// 2. 获取用户订单(等待上一步完成)
const orders = await getOrders(user.id);
console.log('订单数:', orders.length);
// 3. 获取订单详情(等待上一步完成)
const orderDetails = await getOrderDetails(orders[0].id);
console.log('订单详情:', orderDetails);
return { user, orders, orderDetails };
}
//并行执行
// 使用 Promise.all 并行执行
async function fetchAllData() {
// 同时发起所有请求
const [user, orders, settings] = await Promise.all([
getUser(1),
getOrders(1),
getSettings(1)
]);
return { user, orders, settings };
}
// 使用循环处理多个并行请求
async function processMultipleItems(items) {
//错误:顺序执行,效率低
for (const item of items) {
await processItem(item); // 每个都等待
}
//正确:并行执行
const promises = items.map(item => processItem(item));
const results = await Promise.all(promises);
//正确:控制并发数量
await processWithConcurrency(items, 3);
}
(4)实际应用
javascript
// 封装 API 请求
class ApiClient {
constructor(baseURL) {
this.baseURL = baseURL;
}
async request(endpoint, options = {}) {
const url = `${this.baseURL}${endpoint}`;
const defaultOptions = {
headers: {
'Content-Type': 'application/json',
'Authorization': `Bearer ${this.getToken()}`
}
};
try {
const response = await fetch(url, {
...defaultOptions,
...options
});
// 处理 HTTP 状态码
if (response.status === 401) {
// token 过期,尝试刷新
await this.refreshToken();
return this.request(endpoint, options);
}
if (!response.ok) {
throw new Error(`请求失败: ${response.status}`);
}
return await response.json();
} catch (error) {
console.error(`API请求失败 ${endpoint}:`, error);
throw error;
}
}
async get(endpoint, params = {}) {
const queryString = new URLSearchParams(params).toString();
const url = queryString ? `${endpoint}?${queryString}` : endpoint;
return this.request(url);
}
async post(endpoint, data) {
return this.request(endpoint, {
method: 'POST',
body: JSON.stringify(data)
});
}
// 其他方法:put、delete、patch 等
}
// 使用示例
const api = new ApiClient('https://api.example.com');
async function fetchUserData() {
try {
const [user, orders, notifications] = await Promise.all([
api.get('/users/123'),
api.get('/users/123/orders'),
api.get('/users/123/notifications')
]);
return { user, orders, notifications };
} catch (error) {
// 统一错误处理
showErrorToast('获取用户数据失败');
throw error;
}
}
表单提交处理
javascript
// 表单提交优化
async function handleSubmit(event) {
event.preventDefault();
const form = event.target;
const submitButton = form.querySelector('button[type="submit"]');
const originalText = submitButton.textContent;
try {
// 禁用提交按钮,防止重复提交
submitButton.disabled = true;
submitButton.textContent = '提交中...';
// 收集表单数据
const formData = new FormData(form);
const data = Object.fromEntries(formData);
// 验证数据
if (!validateFormData(data)) {
throw new Error('表单验证失败');
}
// 提交数据
const result = await submitFormData(data);
// 显示成功消息
showSuccessMessage('提交成功!');
// 重置表单或跳转
form.reset();
setTimeout(() => {
window.location.href = '/success';
}, 2000);
} catch (error) {
// 显示错误信息
showErrorMessage(error.message);
// 记录错误
console.error('表单提交失败:', error);
trackError(error);
} finally {
// 恢复按钮状态
submitButton.disabled = false;
submitButton.textContent = originalText;
}
}
// 防抖的搜索功能
function createDebouncedSearch() {
let timeoutId;
return async function search(query) {
clearTimeout(timeoutId);
return new Promise(resolve => {
timeoutId = setTimeout(async () => {
if (query.trim().length < 2) {
resolve([]);
return;
}
try {
const results = await searchAPI(query);
resolve(results);
} catch (error) {
console.error('搜索失败:', error);
resolve([]); // 失败时返回空数组
}
}, 300); // 防抖延迟300ms
});
};
}
const debouncedSearch = createDebouncedSearch();
// 输入框事件监听
searchInput.addEventListener('input', async (event) => {
const query = event.target.value;
const results = await debouncedSearch(query);
displaySearchResults(results);
});





