十一、JavaScript核心技术深度 (15题)
66. JavaScript执行机制深度理解
题目: 请详细解释JavaScript的事件循环机制,包括宏任务、微任务的执行顺序。
参考答案:
🎯 JavaScript执行机制核心
JavaScript执行机制的本质是单线程异步处理,通过"执行栈→任务队列→事件循环"来实现非阻塞的异步编程模型。
🔄 1. 事件循环基本原理
执行环境组成:
- 执行栈(Call Stack): 存储函数调用的执行上下文
- 堆内存(Heap): 存储对象和变量的内存空间
- 任务队列(Task Queue): 存储待执行的异步任务
- Web APIs: 浏览器提供的异步API接口
事件循环流程:
- 执行同步代码: 按顺序执行主线程上的同步代码
- 检查微任务队列: 执行栈清空后,优先处理所有微任务
- 执行一个宏任务: 从宏任务队列取出一个任务执行
- 重复循环: 继续检查微任务,然后执行下一个宏任务
- 渲染更新: 在适当时机进行DOM渲染更新
📋 2. 任务分类详解
宏任务(Macro Task):
- setTimeout/setInterval: 定时器任务
- I/O操作: 文件读写、网络请求
- UI事件: 用户交互事件(click、scroll等)
- script标签: 整个脚本的执行
- MessageChannel: 消息通道通信
- setImmediate: Node.js中的立即执行(IE也支持)
微任务(Micro Task):
- Promise.then/catch/finally: Promise回调
- queueMicrotask: 直接添加微任务的API
- MutationObserver: DOM变化监听
- process.nextTick: Node.js中的下一个tick(优先级最高)
- async/await: 本质上是Promise的语法糖
🔍 3. 执行顺序深度分析
基本执行顺序:
console.log('1'); // 同步代码,立即执行
setTimeout(() => {
console.log('2'); // 宏任务,最后执行
}, 0);
Promise.resolve().then(() => {
console.log('3'); // 微任务,优先于宏任务
});
console.log('4'); // 同步代码,立即执行
// 输出顺序: 1 → 4 → 3 → 2
复杂嵌套情况:
console.log('start');
setTimeout(() => {
console.log('timeout1');
Promise.resolve().then(() => {
console.log('promise1');
});
}, 0);
Promise.resolve().then(() => {
console.log('promise2');
setTimeout(() => {
console.log('timeout2');
}, 0);
});
console.log('end');
// 输出: start → end → promise2 → timeout1 → promise1 → timeout2
⚡ 4. 微任务优先级机制
微任务执行特点:
- 批量执行: 一次事件循环中会执行完所有微任务
- 优先级高: 微任务总是优先于宏任务执行
- 可能阻塞: 大量微任务可能阻塞宏任务执行
- 嵌套处理: 微任务中产生的新微任务会在当前循环中执行
Node.js中的特殊情况:
// Node.js中process.nextTick优先级最高
process.nextTick(() => console.log('nextTick'));
Promise.resolve().then(() => console.log('promise'));
setTimeout(() => console.log('timeout'), 0);
// Node.js输出: nextTick → promise → timeout
🎨 5. 渲染时机与优化
浏览器渲染时机:
- 帧率控制: 浏览器通常以60fps的频率渲染(约16.67ms一帧)
- 渲染时机: 在宏任务执行完毕后,微任务队列清空后进行渲染
- 渲染优化: 浏览器会合并多次DOM修改,批量进行渲染
- 阻塞影响: 长时间运行的JavaScript会阻塞渲染
requestAnimationFrame:
// 在下一次重绘前执行,保证动画流畅
requestAnimationFrame(() => {
// 动画逻辑
element.style.transform = 'translateX(100px)';
});
// 比setTimeout更适合动画
setTimeout(() => {
// 可能在渲染中间执行,导致卡顿
}, 16);
🔧 6. 实际应用与最佳实践
异步编程模式:
// 避免回调地狱
async function fetchData() {
try {
const user = await getUserInfo();
const posts = await getUserPosts(user.id);
const comments = await getPostComments(posts[0].id);
return { user, posts, comments };
} catch (error) {
console.error('数据获取失败:', error);
}
}
// 并发处理
async function fetchAllData() {
const [users, posts, comments] = await Promise.all([
getUsers(),
getPosts(),
getComments()
]);
return { users, posts, comments };
}
性能优化策略:
// 使用微任务进行批量更新
let pending = false;
const updates = [];
function batchUpdate(callback) {
updates.push(callback);
if (!pending) {
pending = true;
Promise.resolve().then(() => {
const currentUpdates = updates.splice(0);
currentUpdates.forEach(update => update());
pending = false;
});
}
}
// 使用方式
batchUpdate(() => updateUI1());
batchUpdate(() => updateUI2());
batchUpdate(() => updateUI3());
// 三个更新会在同一个微任务中批量执行
项目应用: 在大型前端应用中,深入理解事件循环机制对于性能优化至关重要。通过合理利用微任务进行批量更新、使用requestAnimationFrame优化动画、避免长任务阻塞等策略,成功将应用的响应性能提升40%,用户交互延迟降低到50ms以内。关键实践包括:建立性能监控体系、实施任务调度优化、合理使用异步编程模式。
67. 闭包与内存管理
题目: 闭包的实现原理是什么?如何避免闭包导致的内存泄漏?
参考答案:
🎯 闭包核心概念
闭包的本质是词法作用域的持久化,通过"函数+环境→作用域链→变量访问"来实现数据封装和状态保持。
🔍 1. 闭包实现原理
词法作用域:
- 静态绑定: 函数的作用域在定义时就确定了
- 作用域链: 内部函数可以访问外部函数的变量
- 环境记录: JavaScript引擎会保存函数定义时的环境
- 生命周期延长: 内部函数的引用会延长外部变量的生命周期
闭包形成条件:
- 函数嵌套: 内部函数引用外部函数的变量
- 函数返回: 内部函数被返回或传递到外部
- 变量引用: 外部变量被内部函数持续引用
- 执行上下文: 外部函数执行完毕但变量仍被引用
💡 2. 闭包应用场景
数据封装:
function createCounter() {
let count = 0; // 私有变量
return {
increment: () => ++count,
decrement: () => --count,
getCount: () => count
};
}
const counter = createCounter();
console.log(counter.getCount()); // 0
counter.increment();
console.log(counter.getCount()); // 1
// count变量无法直接访问,实现了数据封装
模块模式:
const myModule = (function() {
let privateVar = 0;
function privateMethod() {
console.log('私有方法');
}
return {
publicMethod: function() {
privateVar++;
privateMethod();
},
getPrivateVar: function() {
return privateVar;
}
};
})();
函数工厂:
function createMultiplier(multiplier) {
return function(x) {
return x * multiplier;
};
}
const double = createMultiplier(2);
const triple = createMultiplier(3);
console.log(double(5)); // 10
console.log(triple(5)); // 15
🐛 3. 内存泄漏问题
常见泄漏场景:
// 问题1: 意外的全局变量
function createHandler() {
const largeData = new Array(1000000).fill('data');
return function(event) {
// 只使用event,但largeData无法被回收
console.log(event.type);
};
}
// 问题2: DOM节点引用
function attachHandler() {
const element = document.getElementById('button');
const largeData = new Array(1000000);
element.onclick = function() {
// 即使element从DOM中移除,largeData仍被引用
console.log('clicked');
};
}
// 问题3: 定时器中的闭包
function startTimer() {
const largeData = new Array(1000000);
setInterval(function() {
// largeData被定时器函数引用,无法释放
console.log('timer tick');
}, 1000);
}
🔧 4. 内存泄漏解决方案
避免不必要的引用:
// 优化方案1: 只引用需要的变量
function createHandler() {
const largeData = new Array(1000000).fill('data');
const neededValue = largeData[0]; // 只保留需要的值
return function(event) {
console.log(event.type, neededValue);
};
}
// 优化方案2: 及时清理引用
function createHandlerWithCleanup() {
let largeData = new Array(1000000).fill('data');
const handler = function(event) {
console.log(event.type);
};
// 提供清理方法
handler.cleanup = function() {
largeData = null;
};
return handler;
}
使用WeakMap避免强引用:
const privateData = new WeakMap();
class MyClass {
constructor(data) {
// 使用WeakMap存储私有数据
privateData.set(this, { data });
}
getData() {
return privateData.get(this).data;
}
}
// 当实例被垃圾回收时,WeakMap中的数据也会被清理
合理管理事件监听器:
class Component {
constructor() {
this.handlers = [];
this.handleClick = this.handleClick.bind(this);
}
addEventListeners() {
document.addEventListener('click', this.handleClick);
this.handlers.push(['click', this.handleClick]);
}
removeEventListeners() {
this.handlers.forEach(([event, handler]) => {
document.removeEventListener(event, handler);
});
this.handlers = [];
}
handleClick(event) {
console.log('clicked');
}
destroy() {
this.removeEventListeners();
// 清理其他引用
}
}
📊 5. 内存监控与调试
内存使用监控:
function monitorMemoryUsage() {
if (performance.memory) {
const { usedJSHeapSize, totalJSHeapSize, jsHeapSizeLimit } = performance.memory;
console.log({
used: `${(usedJSHeapSize / 1024 / 1024).toFixed(2)} MB`,
total: `${(totalJSHeapSize / 1024 / 1024).toFixed(2)} MB`,
limit: `${(jsHeapSizeLimit / 1024 / 1024).toFixed(2)} MB`
});
}
}
// 定期监控
setInterval(monitorMemoryUsage, 5000);
闭包调试技巧:
function debugClosure() {
const data = { value: 42 };
function inner() {
// 在Chrome DevTools中可以查看Closure作用域
debugger;
console.log(data.value);
}
return inner;
}
const fn = debugClosure();
fn(); // 在debugger处可以查看闭包变量
💡 6. 最佳实践
设计原则:
- 最小引用: 只引用真正需要的变量
- 及时清理: 提供清理机制,及时释放资源
- 避免循环引用: 注意对象间的循环引用
- 使用弱引用: 适当使用WeakMap、WeakSet
性能优化:
// 使用对象池减少闭包创建
class HandlerPool {
constructor() {
this.pool = [];
}
getHandler(data) {
let handler = this.pool.pop();
if (!handler) {
handler = function(event) {
// 处理逻辑
console.log(event.type, this.data);
};
}
handler.data = data;
return handler;
}
releaseHandler(handler) {
handler.data = null;
this.pool.push(handler);
}
}
代码审查要点:
- 检查是否有不必要的变量引用
- 确认事件监听器是否正确移除
- 验证定时器是否正确清理
- 检查DOM节点引用是否及时释放
项目经验: 在大型SPA应用中,通过系统性的闭包优化和内存管理,成功解决了长时间运行后的内存泄漏问题。关键措施包括:建立内存监控体系、实施闭包最佳实践、定期进行内存分析、建立资源清理机制。最终将应用长时间运行的内存增长控制在合理范围内,显著提升了应用稳定性。
68. 原型链与继承机制
题目: 详细解释JavaScript的原型链机制,以及各种继承方式的优缺点。
参考答案:
🎯 原型链核心概念
JavaScript原型链的本质是对象属性查找机制,通过"对象→原型→原型链→继承"来实现面向对象编程的继承特性。
🔗 1. 原型链基本原理
核心概念:
- prototype: 函数的原型对象,包含共享的属性和方法
- proto: 对象的原型引用,指向构造函数的prototype
- constructor: 原型对象的构造函数引用
- 原型链: 通过__proto__连接的对象链条
查找机制:
function Person(name) {
this.name = name;
}
Person.prototype.sayHello = function() {
console.log(`Hello, I'm ${this.name}`);
};
const person = new Person('Alice');
// 属性查找顺序:
// 1. person.sayHello -> 在person对象上查找
// 2. person.__proto__.sayHello -> 在Person.prototype上查找 ✓
// 3. person.__proto__.__proto__ -> 在Object.prototype上查找
// 4. person.__proto__.__proto__.__proto__ -> null,查找结束
🏗️ 2. 继承方式详解
原型链继承:
function Animal(name) {
this.name = name;
this.colors = ['red', 'blue'];
}
Animal.prototype.eat = function() {
console.log(`${this.name} is eating`);
};
function Dog(name, breed) {
this.breed = breed;
}
// 原型链继承
Dog.prototype = new Animal();
Dog.prototype.constructor = Dog;
const dog1 = new Dog('Buddy', 'Golden');
const dog2 = new Dog('Max', 'Labrador');
// 问题: 引用类型属性被共享
dog1.colors.push('green');
console.log(dog2.colors); // ['red', 'blue', 'green'] - 被污染了
构造函数继承:
function Animal(name) {
this.name = name;
this.colors = ['red', 'blue'];
}
Animal.prototype.eat = function() {
console.log(`${this.name} is eating`);
};
function Dog(name, breed) {
// 构造函数继承
Animal.call(this, name);
this.breed = breed;
}
const dog1 = new Dog('Buddy', 'Golden');
const dog2 = new Dog('Max', 'Labrador');
// 优点: 避免引用类型共享
dog1.colors.push('green');
console.log(dog2.colors); // ['red', 'blue'] - 独立的
// 缺点: 无法继承原型方法
dog1.eat(); // TypeError: dog1.eat is not a function
组合继承:
function Animal(name) {
this.name = name;
this.colors = ['red', 'blue'];
}
Animal.prototype.eat = function() {
console.log(`${this.name} is eating`);
};
function Dog(name, breed) {
// 构造函数继承 - 继承实例属性
Animal.call(this, name);
this.breed = breed;
}
// 原型链继承 - 继承原型方法
Dog.prototype = new Animal();
Dog.prototype.constructor = Dog;
Dog.prototype.bark = function() {
console.log(`${this.name} is barking`);
};
const dog = new Dog('Buddy', 'Golden');
dog.eat(); // 可以调用父类方法
dog.bark(); // 可以调用子类方法
// 缺点: 调用了两次父类构造函数
寄生组合继承:
function Animal(name) {
this.name = name;
this.colors = ['red', 'blue'];
}
Animal.prototype.eat = function() {
console.log(`${this.name} is eating`);
};
function Dog(name, breed) {
Animal.call(this, name);
this.breed = breed;
}
// 寄生组合继承 - 最佳方案
function inheritPrototype(child, parent) {
const prototype = Object.create(parent.prototype);
prototype.constructor = child;
child.prototype = prototype;
}
inheritPrototype(Dog, Animal);
Dog.prototype.bark = function() {
console.log(`${this.name} is barking`);
};
// 优点: 只调用一次父类构造函数,效率最高
🆕 3. ES6 Class继承
Class语法:
class Animal {
constructor(name) {
this.name = name;
this.colors = ['red', 'blue'];
}
eat() {
console.log(`${this.name} is eating`);
}
static getSpecies() {
return 'Animal';
}
}
class Dog extends Animal {
constructor(name, breed) {
super(name); // 调用父类构造函数
this.breed = breed;
}
bark() {
console.log(`${this.name} is barking`);
}
eat() {
super.eat(); // 调用父类方法
console.log('Dog finished eating');
}
}
const dog = new Dog('Buddy', 'Golden');
dog.eat(); // 可以调用父类和子类方法
Class vs 函数构造器对比:
// 函数构造器
function Person(name) {
this.name = name;
}
Person.prototype.greet = function() {
console.log(`Hello, ${this.name}`);
};
// Class语法 - 本质上是语法糖
class PersonClass {
constructor(name) {
this.name = name;
}
greet() {
console.log(`Hello, ${this.name}`);
}
}
// 两者创建的对象在原型链上是相同的
console.log(PersonClass.prototype.constructor === PersonClass); // true
🔧 4. 高级继承技巧
Mixin模式:
// 定义Mixin
const Flyable = {
fly() {
console.log(`${this.name} is flying`);
}
};
const Swimmable = {
swim() {
console.log(`${this.name} is swimming`);
}
};
// 应用Mixin
class Duck extends Animal {
constructor(name) {
super(name);
}
}
// 混入多个能力
Object.assign(Duck.prototype, Flyable, Swimmable);
const duck = new Duck('Donald');
duck.fly(); // Donald is flying
duck.swim(); // Donald is swimming
工厂函数模式:
function createAnimal(name, type) {
const animal = {
name,
type,
eat() {
console.log(`${this.name} is eating`);
}
};
// 根据类型添加特定方法
if (type === 'bird') {
animal.fly = function() {
console.log(`${this.name} is flying`);
};
} else if (type === 'fish') {
animal.swim = function() {
console.log(`${this.name} is swimming`);
};
}
return animal;
}
const bird = createAnimal('Tweety', 'bird');
const fish = createAnimal('Nemo', 'fish');
🛠️ 5. 原型操作方法
Object.create():
// 创建具有指定原型的对象
const animalPrototype = {
eat() {
console.log('eating...');
}
};
const dog = Object.create(animalPrototype);
dog.name = 'Buddy';
dog.eat(); // eating...
// 等价于
const dog2 = {};
dog2.__proto__ = animalPrototype;
Object.setPrototypeOf():
const animal = {
eat() {
console.log('eating...');
}
};
const dog = {
name: 'Buddy'
};
// 设置原型
Object.setPrototypeOf(dog, animal);
dog.eat(); // eating...
hasOwnProperty()和in操作符:
function Person(name) {
this.name = name;
}
Person.prototype.age = 25;
const person = new Person('Alice');
console.log('name' in person); // true
console.log('age' in person); // true
console.log(person.hasOwnProperty('name')); // true
console.log(person.hasOwnProperty('age')); // false
📊 6. 性能考虑
原型链长度影响:
// 避免过长的原型链
class A {}
class B extends A {}
class C extends B {}
class D extends C {}
class E extends D {} // 原型链过长,影响属性查找性能
// 推荐: 保持合理的继承层次
属性访问优化:
// 频繁访问的属性应该在实例上
class OptimizedClass {
constructor() {
// 将常用方法绑定到实例
this.frequentMethod = this.frequentMethod.bind(this);
}
frequentMethod() {
// 频繁调用的方法
}
rareMethod() {
// 不常用的方法保持在原型上
}
}
💡 7. 最佳实践
继承设计原则:
- 优先组合: 优先使用组合而非继承
- 浅层继承: 保持继承层次的简单
- 接口隔离: 使用Mixin实现多重继承
- 里氏替换: 子类应该能够替换父类
现代JavaScript推荐:
// 推荐: 使用Class语法
class Animal {
constructor(name) {
this.name = name;
}
eat() {
console.log(`${this.name} is eating`);
}
}
class Dog extends Animal {
constructor(name, breed) {
super(name);
this.breed = breed;
}
bark() {
console.log(`${this.name} is barking`);
}
}
// 或者使用工厂函数 + 组合
function createDog(name, breed) {
return {
name,
breed,
eat() {
console.log(`${this.name} is eating`);
},
bark() {
console.log(`${this.name} is barking`);
}
};
}
项目经验: 在大型项目中,合理的继承设计对代码维护性至关重要。通过采用ES6 Class语法结合Mixin模式,成功构建了灵活的组件继承体系。关键实践包括:保持浅层继承结构、使用组合优于继承的设计原则、建立清晰的接口规范、实施严格的代码审查机制。最终实现了代码复用率提升30%,维护成本降低40%的效果。
69. 异步编程深度解析
题目: 从回调函数到Promise再到async/await,详细分析JavaScript异步编程的演进和最佳实践。
参考答案:
🎯 异步编程核心价值
JavaScript异步编程的本质是非阻塞执行,通过"回调→Promise→async/await→并发控制"的演进来解决复杂的异步操作管理问题。
📈 1. 异步编程演进历程
回调函数时代:
// 回调地狱的典型例子
function getUserData(userId, callback) {
setTimeout(() => {
console.log('获取用户信息');
callback(null, { id: userId, name: 'Alice' });
}, 1000);
}
function getUserPosts(userId, callback) {
setTimeout(() => {
console.log('获取用户文章');
callback(null, [{ id: 1, title: 'Post 1' }]);
}, 1000);
}
function getPostComments(postId, callback) {
setTimeout(() => {
console.log('获取文章评论');
callback(null, [{ id: 1, content: 'Great post!' }]);
}, 1000);
}
// 回调地狱
getUserData(1, (err, user) => {
if (err) return console.error(err);
getUserPosts(user.id, (err, posts) => {
if (err) return console.error(err);
getPostComments(posts[0].id, (err, comments) => {
if (err) return console.error(err);
console.log('完成所有数据获取', { user, posts, comments });
});
});
});
Promise时代:
// Promise化改造
function getUserDataPromise(userId) {
return new Promise((resolve, reject) => {
setTimeout(() => {
console.log('获取用户信息');
resolve({ id: userId, name: 'Alice' });
}, 1000);
});
}
function getUserPostsPromise(userId) {
return new Promise((resolve, reject) => {
setTimeout(() => {
console.log('获取用户文章');
resolve([{ id: 1, title: 'Post 1' }]);
}, 1000);
});
}
function getPostCommentsPromise(postId) {
return new Promise((resolve, reject) => {
setTimeout(() => {
console.log('获取文章评论');
resolve([{ id: 1, content: 'Great post!' }]);
}, 1000);
});
}
// Promise链式调用
getUserDataPromise(1)
.then(user => {
return getUserPostsPromise(user.id)
.then(posts => ({ user, posts }));
})
.then(({ user, posts }) => {
return getPostCommentsPromise(posts[0].id)
.then(comments => ({ user, posts, comments }));
})
.then(result => {
console.log('完成所有数据获取', result);
})
.catch(error => {
console.error('发生错误:', error);
});
async/await时代:
// async/await优雅解决方案
async function fetchAllData(userId) {
try {
console.log('开始获取数据...');
const user = await getUserDataPromise(userId);
const posts = await getUserPostsPromise(user.id);
const comments = await getPostCommentsPromise(posts[0].id);
console.log('完成所有数据获取', { user, posts, comments });
return { user, posts, comments };
} catch (error) {
console.error('数据获取失败:', error);
throw error;
}
}
// 使用
fetchAllData(1);
⚡ 2. 并发控制与优化
并行执行:
// 串行执行 - 耗时3秒
async function serialExecution() {
const start = Date.now();
const user = await getUserDataPromise(1);
const posts = await getUserPostsPromise(1);
const comments = await getPostCommentsPromise(1);
console.log(`串行执行耗时: ${Date.now() - start}ms`);
return { user, posts, comments };
}
// 并行执行 - 耗时1秒
async function parallelExecution() {
const start = Date.now();
const [user, posts, comments] = await Promise.all([
getUserDataPromise(1),
getUserPostsPromise(1),
getPostCommentsPromise(1)
]);
console.log(`并行执行耗时: ${Date.now() - start}ms`);
return { user, posts, comments };
}
错误处理策略:
// Promise.allSettled - 处理部分失败
async function robustDataFetch() {
const results = await Promise.allSettled([
getUserDataPromise(1),
getUserPostsPromise(1),
getPostCommentsPromise(1)
]);
const successResults = results
.filter(result => result.status === 'fulfilled')
.map(result => result.value);
const errors = results
.filter(result => result.status === 'rejected')
.map(result => result.reason);
if (errors.length > 0) {
console.warn('部分请求失败:', errors);
}
return successResults;
}
// 重试机制
async function fetchWithRetry(fn, maxRetries = 3, delay = 1000) {
for (let i = 0; i < maxRetries; i++) {
try {
return await fn();
} catch (error) {
console.log(`第${i + 1}次尝试失败:`, error.message);
if (i === maxRetries - 1) {
throw error;
}
await new Promise(resolve => setTimeout(resolve, delay));
}
}
}
🔄 3. 高级异步模式
生成器与异步迭代:
// 异步生成器
async function* asyncGenerator() {
let i = 0;
while (i < 5) {
await new Promise(resolve => setTimeout(resolve, 1000));
yield i++;
}
}
// 使用异步迭代
async function consumeAsyncGenerator() {
for await (const value of asyncGenerator()) {
console.log('生成器产生值:', value);
}
}
// 自定义异步迭代器
class AsyncRange {
constructor(start, end) {
this.start = start;
this.end = end;
}
async *[Symbol.asyncIterator]() {
for (let i = this.start; i <= this.end; i++) {
await new Promise(resolve => setTimeout(resolve, 500));
yield i;
}
}
}
// 使用
async function useAsyncRange() {
for await (const num of new AsyncRange(1, 5)) {
console.log('异步范围值:', num);
}
}
流式处理:
// 异步数据流处理
class AsyncDataStream {
constructor(source) {
this.source = source;
}
async *map(fn) {
for await (const item of this.source) {
yield await fn(item);
}
}
async *filter(predicate) {
for await (const item of this.source) {
if (await predicate(item)) {
yield item;
}
}
}
async collect() {
const results = [];
for await (const item of this.source) {
results.push(item);
}
return results;
}
}
// 使用流式处理
async function processDataStream() {
const numbers = async function*() {
for (let i = 1; i <= 10; i++) {
await new Promise(resolve => setTimeout(resolve, 100));
yield i;
}
};
const stream = new AsyncDataStream(numbers())
.filter(async x => x % 2 === 0)
.map(async x => x * 2);
const results = await stream.collect();
console.log('流处理结果:', results); // [4, 8, 12, 16, 20]
}
🎛️ 4. 并发控制机制
限制并发数量:
class ConcurrencyController {
constructor(limit) {
this.limit = limit;
this.running = 0;
this.queue = [];
}
async execute(asyncFn) {
return new Promise((resolve, reject) => {
this.queue.push({
asyncFn,
resolve,
reject
});
this.process();
});
}
async process() {
if (this.running >= this.limit || this.queue.length === 0) {
return;
}
this.running++;
const { asyncFn, resolve, reject } = this.queue.shift();
try {
const result = await asyncFn();
resolve(result);
} catch (error) {
reject(error);
} finally {
this.running--;
this.process();
}
}
}
// 使用并发控制
async function controlledConcurrency() {
const controller = new ConcurrencyController(3); // 最多3个并发
const tasks = Array.from({ length: 10 }, (_, i) =>
() => new Promise(resolve =>
setTimeout(() => resolve(`任务${i + 1}完成`), Math.random() * 2000)
)
);
const results = await Promise.all(
tasks.map(task => controller.execute(task))
);
console.log('所有任务完成:', results);
}
信号量控制:
class Semaphore {
constructor(count) {
this.count = count;
this.waiting = [];
}
async acquire() {
if (this.count > 0) {
this.count--;
return;
}
return new Promise(resolve => {
this.waiting.push(resolve);
});
}
release() {
if (this.waiting.length > 0) {
const resolve = this.waiting.shift();
resolve();
} else {
this.count++;
}
}
async execute(asyncFn) {
await this.acquire();
try {
return await asyncFn();
} finally {
this.release();
}
}
}
// 使用信号量
const semaphore = new Semaphore(2);
async function limitedResourceAccess() {
const tasks = Array.from({ length: 5 }, (_, i) =>
semaphore.execute(async () => {
console.log(`任务${i + 1}开始执行`);
await new Promise(resolve => setTimeout(resolve, 2000));
console.log(`任务${i + 1}执行完成`);
return `结果${i + 1}`;
})
);
const results = await Promise.all(tasks);
console.log('所有任务结果:', results);
}
🔧 5. 实际应用场景
API请求管理:
class APIManager {
constructor() {
this.cache = new Map();
this.pendingRequests = new Map();
}
async request(url, options = {}) {
// 缓存检查
if (this.cache.has(url)) {
return this.cache.get(url);
}
// 防止重复请求
if (this.pendingRequests.has(url)) {
return this.pendingRequests.get(url);
}
// 发起请求
const requestPromise = this.fetchData(url, options);
this.pendingRequests.set(url, requestPromise);
try {
const result = await requestPromise;
this.cache.set(url, result);
return result;
} finally {
this.pendingRequests.delete(url);
}
}
async fetchData(url, options) {
const response = await fetch(url, options);
if (!response.ok) {
throw new Error(`HTTP ${response.status}: ${response.statusText}`);
}
return response.json();
}
}
批量操作优化:
class BatchProcessor {
constructor(batchSize = 10, delay = 100) {
this.batchSize = batchSize;
this.delay = delay;
this.queue = [];
this.processing = false;
}
async add(item) {
return new Promise((resolve, reject) => {
this.queue.push({ item, resolve, reject });
this.scheduleProcess();
});
}
scheduleProcess() {
if (!this.processing) {
this.processing = true;
setTimeout(() => this.process(), this.delay);
}
}
async process() {
while (this.queue.length > 0) {
const batch = this.queue.splice(0, this.batchSize);
try {
const results = await this.processBatch(
batch.map(item => item.item)
);
batch.forEach((item, index) => {
item.resolve(results[index]);
});
} catch (error) {
batch.forEach(item => {
item.reject(error);
});
}
}
this.processing = false;
}
async processBatch(items) {
// 批量处理逻辑
console.log(`批量处理 ${items.length} 个项目`);
await new Promise(resolve => setTimeout(resolve, 1000));
return items.map(item => `处理结果: ${item}`);
}
}
项目经验: 在大型前端应用中,合理的异步编程模式对性能和用户体验至关重要。通过采用async/await结合并发控制、实施请求去重和缓存机制、建立错误重试和降级策略,成功将接口响应时间优化50%,用户体验显著提升。关键实践包括:建立统一的异步操作规范、实施细粒度的并发控制、完善的错误处理机制、性能监控和优化。
祝您面试顺利!
2万+

被折叠的 条评论
为什么被折叠?



