引言
在前端开发中,性能优化不仅仅是使用现成的库和工具,理解其底层原理并能够手写实现是关键。通过手写这些优化技术,我们可以:
- 更深入地理解性能瓶颈
- 根据具体场景定制优化方案
- 避免引入不必要的依赖
- 提升解决问题的能力
本文将深入探讨JavaScript性能优化的核心手写实现,每个技术点都将包含完整的实现代码和应用场景。
一、虚拟列表实现(Virtual List)
虚拟列表是处理大数据列表渲染的核心技术,通过只渲染可视区域内的元素来大幅提升性能。
1.1 核心原理
javascript
class VirtualList {
constructor(options) {
this.container = options.container;
this.itemHeight = options.itemHeight;
this.totalItems = options.totalItems;
this.bufferSize = options.bufferSize || 5; // 上下缓冲区域
this.renderItem = options.renderItem;
this.visibleItems = [];
this.startIndex = 0;
this.endIndex = 0;
this.init();
}
init() {
// 创建容器
this.viewport = document.createElement('div');
this.viewport.style.position = 'relative';
this.viewport.style.height = `${this.totalItems * this.itemHeight}px`;
this.viewport.style.overflow = 'hidden';
this.content = document.createElement('div');
this.content.style.position = 'absolute';
this.content.style.top = '0';
this.content.style.left = '0';
this.content.style.width = '100%';
this.container.appendChild(this.viewport);
this.viewport.appendChild(this.content);
// 绑定滚动事件
this.viewport.addEventListener('scroll', this.handleScroll.bind(this));
// 初始渲染
this.calculateVisibleRange();
this.renderVisibleItems();
}
calculateVisibleRange() {
const scrollTop = this.viewport.scrollTop;
const visibleHeight = this.viewport.clientHeight;
// 计算可视区域起始和结束索引
this.startIndex = Math.max(
0,
Math.floor(scrollTop / this.itemHeight) - this.bufferSize
);
this.endIndex = Math.min(
this.totalItems - 1,
Math.ceil((scrollTop + visibleHeight) / this.itemHeight) + this.bufferSize
);
}
renderVisibleItems() {
// 移除不在可视区域的元素
this.visibleItems.forEach(item => {
if (item.index < this.startIndex || item.index > this.endIndex) {
item.element.remove();
}
});
// 更新可见项数组
this.visibleItems = this.visibleItems.filter(
item => item.index >= this.startIndex && item.index <= this.endIndex
);
// 创建新的可见项
for (let i = this.startIndex; i <= this.endIndex; i++) {
const existingItem = this.visibleItems.find(item => item.index === i);
if (!existingItem) {
const itemElement = document.createElement('div');
itemElement.style.position = 'absolute';
itemElement.style.top = `${i * this.itemHeight}px`;
itemElement.style.height = `${this.itemHeight}px`;
itemElement.style.width = '100%';
this.renderItem(itemElement, i);
this.content.appendChild(itemElement);
this.visibleItems.push({ index: i, element: itemElement });
}
}
// 更新内容区域位置
this.content.style.transform = `translateY(${this.startIndex * this.itemHeight}px)`;
}
handleScroll() {
requestAnimationFrame(() => {
this.calculateVisibleRange();
this.renderVisibleItems();
});
}
updateItem(index, data) {
const item = this.visibleItems.find(item => item.index === index);
if (item) {
this.renderItem(item.element, index, data);
}
}
destroy() {
this.viewport.removeEventListener('scroll', this.handleScroll);
this.container.removeChild(this.viewport);
}
}
// 使用示例
const listContainer = document.getElementById('list-container');
const virtualList = new VirtualList({
container: listContainer,
itemHeight: 50,
totalItems: 10000,
bufferSize: 10,
renderItem: (element, index) => {
element.textContent = `Item ${index + 1}`;
element.style.borderBottom = '1px solid #eee';
element.style.padding = '10px';
}
});
// 动态更新
setTimeout(() => {
virtualList.updateItem(5, 'Updated Item 6');
}, 2000);
1.2 带动态高度的虚拟列表
javascript
class DynamicVirtualList {
constructor(options) {
this.container = options.container;
this.totalItems = options.totalItems;
this.renderItem = options.renderItem;
this.estimateHeight = options.estimateHeight || 50;
this.bufferSize = options.bufferSize || 5;
this.itemHeights = new Array(this.totalItems).fill(null);
this.itemPositions = new Array(this.totalItems).fill(0);
this.visibleItems = [];
this.cachedItems = new Map();
this.init();
}
init() {
this.viewport = document.createElement('div');
this.viewport.style.position = 'relative';
this.viewport.style.height = '500px';
this.viewport.style.overflow = 'auto';
this.content = document.createElement('div');
this.content.style.position = 'relative';
this.viewport.appendChild(this.content);
this.container.appendChild(this.viewport);
// 计算预估的总高度
this.calculatePositions();
this.updateContentHeight();
this.viewport.addEventListener('scroll', this.handleScroll.bind(this));
// 初始渲染
this.calculateVisibleRange();
this.renderVisibleItems();
}
calculatePositions() {
let totalHeight = 0;
for (let i = 0; i < this.totalItems; i++) {
this.itemPositions[i] = totalHeight;
totalHeight += this.itemHeights[i] || this.estimateHeight;
}
this.totalHeight = totalHeight;
}
updateContentHeight() {
this.content.style.height = `${this.totalHeight}px`;
}
calculateVisibleRange() {
const scrollTop = this.viewport.scrollTop;
const viewportHeight = this.viewport.clientHeight;
// 二分查找起始索引
let start = 0;
let end = this.totalItems - 1;
while (start <= end) {
const mid = Math.floor((start + end) / 2);
if (this.itemPositions[mid] <= scrollTop) {
start = mid + 1;
} else {
end = mid - 1;
}
}
this.startIndex = Math.max(0, end - this.bufferSize);
// 查找结束索引
let currentHeight = scrollTop;
this.endIndex = this.startIndex;
while (
this.endIndex < this.totalItems &&
currentHeight < scrollTop + viewportHeight
) {
currentHeight += this.itemHeights[this.endIndex] || this.estimateHeight;
this.endIndex++;
}
this.endIndex = Math.min(
this.totalItems - 1,
this.endIndex + this.bufferSize
);
}
renderVisibleItems() {
// 更新可见项
const newVisibleItems = [];
for (let i = this.startIndex; i <= this.endIndex; i++) {
let itemElement = this.cachedItems.get(i);
if (!itemElement) {
itemElement = document.createElement('div');
itemElement.style.position = 'absolute';
itemElement.style.top = `${this.itemPositions[i]}px`;
itemElement.style.width = '100%';
this.renderItem(itemElement, i);
this.cachedItems.set(i, itemElement);
this.content.appendChild(itemElement);
// 测量实际高度
if (this.itemHeights[i] === null) {
this.itemHeights[i] = itemElement.offsetHeight;
this.calculatePositions();
this.updateContentHeight();
// 重新计算位置
itemElement.style.top = `${this.itemPositions[i]}px`;
}
}
newVisibleItems.push({ index: i, element: itemElement });
}
// 隐藏不在可视区域的元素
this.visibleItems.forEach(({ index, element }) => {
if (index < this.startIndex || index > this.endIndex) {
element.style.display = 'none';
}
});
// 显示可见元素
newVisibleItems.forEach(({ index, element }) => {
element.style.display = '';
element.style.top = `${this.itemPositions[index]}px`;
});
this.visibleItems = newVisibleItems;
}
handleScroll() {
requestAnimationFrame(() => {
this.calculateVisibleRange();
this.renderVisibleItems();
});
}
updateItem(index, data) {
const itemElement = this.cachedItems.get(index);
if (itemElement) {
const oldHeight = this.itemHeights[index] || this.estimateHeight;
this.renderItem(itemElement, index, data);
const newHeight = itemElement.offsetHeight;
if (oldHeight !== newHeight) {
this.itemHeights[index] = newHeight;
this.calculatePositions();
this.updateContentHeight();
this.renderVisibleItems();
}
}
}
}
二、图片懒加载(Lazy Loading)
2.1 基于IntersectionObserver的实现
javascript
class LazyImageLoader {
constructor(options = {}) {
this.options = {
root: null,
rootMargin: '0px',
threshold: 0.1,
placeholder: 'data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7',
errorImage: null,
loadingClass: 'lazy-loading',
loadedClass: 'lazy-loaded',
errorClass: 'lazy-error',
...options
};
this.images = new Map();
this.observer = null;
this.fallbackTimeout = 3000; // 降级超时时间
this.init();
}
init() {
if ('IntersectionObserver' in window) {
this.observer = new IntersectionObserver(
this.handleIntersection.bind(this),
this.options
);
} else {
this.useFallback();
}
// 预连接DNS和预加载
this.addPreconnect();
}
addPreconnect() {
const domains = new Set();
// 收集所有图片的域名
document.querySelectorAll('img[data-src]').forEach(img => {
const src = img.getAttribute('data-src');
if (src) {
try {
const url = new URL(src, window.location.origin);
domains.add(url.origin);
} catch (e) {
console.warn('Invalid URL:', src);
}
}
});
// 添加preconnect链接
domains.forEach(domain => {
const link = document.createElement('link');
link.rel = 'preconnect';
link.href = domain;
link.crossOrigin = 'anonymous';
document.head.appendChild(link);
});
}
registerImage(imgElement) {
if (!(imgElement instanceof HTMLImageElement)) {
throw new Error('Element must be an image');
}
const src = imgElement.getAttribute('data-src');
if (!src) return;
// 保存原始属性
imgElement.setAttribute('data-lazy-src', src);
// 设置占位符
if (imgElement.src !== this.options.placeholder) {
imgElement.setAttribute('data-original-src', imgElement.src);
imgElement.src = this.options.placeholder;
}
imgElement.classList.add(this.options.loadingClass);
// 添加到观察列表
this.images.set(imgElement, {
src,
loaded: false,
loadAttempted: false,
observerAttached: false
});
this.attachObserver(imgElement);
}
attachObserver(imgElement) {
if (this.observer && !this.images.get(imgElement)?.observerAttached) {
this.observer.observe(imgElement);
this.images.get(imgElement).observerAttached = true;
}
}
handleIntersection(entries) {
entries.forEach(entry => {
if (entry.isIntersecting) {
const img = entry.target;
this.loadImage(img);
this.observer?.unobserve(img);
}
});
}
async loadImage(imgElement) {
const imageData = this.images.get(imgElement);
if (!imageData || imageData.loadAttempted) return;
imageData.loadAttempted = true;
// 移除加载类,添加加载中类
imgElement.classList.remove(this.options.loadingClass);
imgElement.classList.add(this.options.loadingClass);
// 创建加载超时
const loadTimeout = setTimeout(() => {
if (!imageData.loaded) {
this.handleImageError(imgElement, new Error('Image load timeout'));
}
}, this.fallbackTimeout);
try {
// 预加载图片
await this.preloadImage(imageData.src);
// 应用图片
this.applyImage(imgElement, imageData.src);
clearTimeout(loadTimeout);
// 更新状态
imageData.loaded = true;
imgElement.classList.remove(this.options.loadingClass);
imgElement.classList.add(this.options.loadedClass);
// 触发事件
this.dispatchEvent(imgElement, 'lazyload', { src: imageData.src });
// 预加载相邻图片
this.preloadAdjacentImages(imgElement);
} catch (error) {
this.handleImageError(imgElement, error);
}
}
async preloadImage(src) {
return new Promise((resolve, reject) => {
const img = new Image();
img.onload = () => {
img.onload = img.onerror = null;
resolve(img);
};
img.onerror = (err) => {
img.onload = img.onerror = null;
reject(new Error(`Failed to load image: ${src}`));
};
// 设置crossOrigin属性
if (src.startsWith('http')) {
img.crossOrigin = 'anonymous';
}
img.src = src;
});
}
applyImage(imgElement, src) {
// 使用requestAnimationFrame确保流畅
requestAnimationFrame(() => {
imgElement.src = src;
// 如果有srcset也更新
const srcset = imgElement.getAttribute('data-srcset');
if (srcset) {
imgElement.srcset = srcset;
imgElement.removeAttribute('data-srcset');
}
// 移除data-src属性
imgElement.removeAttribute('data-src');
imgElement.removeAttribute('data-lazy-src');
});
}
preloadAdjacentImages(currentImg) {
const allImages = Array.from(this.images.keys());
const currentIndex = allImages.indexOf(currentImg);
if (currentIndex !== -1) {
// 预加载前后各2张图片
const indices = [
currentIndex - 2, currentIndex - 1,
currentIndex + 1, currentIndex + 2
];
indices.forEach(index => {
if (index >= 0 && index < allImages.length) {
const img = allImages[index];
const imgData = this.images.get(img);
if (!imgData.loaded && !imgData.loadAttempted) {
this.attachObserver(img);
}
}
});
}
}
handleImageError(imgElement, error) {
const imageData = this.images.get(imgElement);
imgElement.classList.remove(this.options.loadingClass);
imgElement.classList.add(this.options.errorClass);
// 设置错误图片
if (this.options.errorImage) {
imgElement.src = this.options.errorImage;
}
// 恢复原始图片(如果有)
const originalSrc = imgElement.getAttribute('data-original-src');
if (originalSrc && originalSrc !== this.options.placeholder) {
imgElement.src = originalSrc;
}
console.error('Lazy image load error:', error);
this.dispatchEvent(imgElement, 'lazyloaderror', {
src: imageData?.src,
error
});
}
dispatchEvent(element, eventName, detail) {
const event = new CustomEvent(eventName, {
bubbles: true,
detail
});
element.dispatchEvent(event);
}
useFallback() {
// 降级方案:滚动监听
window.addEventListener('scroll', this.handleScrollFallback.bind(this));
window.addEventListener('resize', this.handleScrollFallback.bind(this));
window.addEventListener('orientationchange', this.handleScrollFallback.bind(this));
// 初始检查
setTimeout(() => this.handleScrollFallback(), 100);
}
handleScrollFallback() {
const viewportHeight = window.innerHeight;
const scrollTop = window.scrollY;
this.images.forEach((imageData, imgElement) => {
if (!imageData.loaded && !imageData.loadAttempted) {
const rect = imgElement.getBoundingClientRect();
const elementTop = rect.top + scrollTop;
const elementBottom = rect.bottom + scrollTop;
// 判断是否在可视区域内(带缓冲区)
if (
elementBottom >= scrollTop - 500 &&
elementTop <= scrollTop + viewportHeight + 500
) {
this.loadImage(imgElement);
}
}
});
}
// 批量注册
registerAll(selector = 'img[data-src]') {
const images = document.querySelectorAll(selector);
images.forEach(img => this.registerImage(img));
// 监听动态添加的图片
if ('MutationObserver' in window) {
this.mutationObserver = new MutationObserver((mutations) => {
mutations.forEach(mutation => {
mutation.addedNodes.forEach(node => {
if (node.nodeType === 1) { // 元素节点
if (node.matches && node.matches(selector)) {
this.registerImage(node);
}
if (node.querySelectorAll) {
node.querySelectorAll(selector).forEach(img => {
this.registerImage(img);
});
}
}
});
});
});
this.mutationObserver.observe(document.body, {
childList: true,
subtree: true
});
}
}
// 手动触发加载
loadImageNow(imgElement) {
if (this.images.has(imgElement)) {
this.loadImage(imgElement);
}
}
// 销毁
destroy() {
if (this.observer) {
this.observer.disconnect();
}
if (this.mutationObserver) {
this.mutationObserver.disconnect();
}
window.removeEventListener('scroll', this.handleScrollFallback);
window.removeEventListener('resize', this.handleScrollFallback);
window.removeEventListener('orientationchange', this.handleScrollFallback);
this.images.clear();
}
}
// 使用示例
const lazyLoader = new LazyImageLoader({
threshold: 0.01,
placeholder: '/path/to/placeholder.jpg',
errorImage: '/path/to/error.jpg'
});
// 注册所有懒加载图片
document.addEventListener('DOMContentLoaded', () => {
lazyLoader.registerAll();
});
// 动态添加图片
const newImage = document.createElement('img');
newImage.setAttribute('data-src', '/path/to/image.jpg');
document.body.appendChild(newImage);
lazyLoader.registerImage(newImage);
2.2 背景图片懒加载
javascript
class LazyBackgroundLoader extends LazyImageLoader {
constructor(options = {}) {
super(options);
this.attributeName = options.attributeName || 'data-bg';
}
registerElement(element) {
const bgSrc = element.getAttribute(this.attributeName);
if (!bgSrc) return;
this.images.set(element, {
src: bgSrc,
loaded: false,
loadAttempted: false,
observerAttached: false
});
this.attachObserver(element);
}
async loadImage(element) {
const elementData = this.images.get(element);
if (!elementData || elementData.loadAttempted) return;
elementData.loadAttempted = true;
try {
await this.preloadImage(elementData.src);
requestAnimationFrame(() => {
element.style.backgroundImage = `url("${elementData.src}")`;
element.removeAttribute(this.attributeName);
elementData.loaded = true;
element.classList.add(this.options.loadedClass);
this.dispatchEvent(element, 'lazyload', { src: elementData.src });
});
} catch (error) {
this.handleImageError(element, error);
}
}
registerAll(selector = `[${this.attributeName}]`) {
const elements = document.querySelectorAll(selector);
elements.forEach(el => this.registerElement(el));
}
}
三、函数记忆化(Memoization)
3.1 基础记忆化实现
javascript
function memoize(fn, options = {}) {
const {
maxSize = Infinity,
keyResolver = (...args) => JSON.stringify(args),
ttl = null, // 生存时间(毫秒)
cache = new Map()
} = options;
const stats = {
hits: 0,
misses: 0,
size: 0
};
// 创建LRU缓存(最近最少使用)
const lruKeys = [];
const memoized = function(...args) {
const key = keyResolver(...args);
// 检查缓存
if (cache.has(key)) {
const entry = cache.get(key);
// 检查TTL
if (ttl && Date.now() - entry.timestamp > ttl) {
cache.delete(key);
stats.size--;
stats.misses++;
} else {
// 更新LRU顺序
if (maxSize < Infinity) {
const index = lruKeys.indexOf(key);
if (index > -1) {
lruKeys.splice(index, 1);
lruKeys.unshift(key);
}
}
stats.hits++;
return entry.value;
}
}
// 计算新值
stats.misses++;
const result = fn.apply(this, args);
// 缓存结果
const entry = {
value: result,
timestamp: Date.now()
};
cache.set(key, entry);
stats.size++;
// 处理LRU缓存
if (maxSize < Infinity) {
lruKeys.unshift(key);
if (cache.size > maxSize) {
const lruKey = lruKeys.pop();
cache.delete(lruKey);
stats.size--;
}
}
return result;
};
// 添加工具方法
memoized.clear = function() {
cache.clear();
lruKeys.length = 0;
stats.hits = stats.misses = stats.size = 0;
};
memoized.delete = function(...args) {
const key = keyResolver(...args);
const deleted = cache.delete(key);
if (deleted) {
const index = lruKeys.indexOf(key);
if (index > -1) lruKeys.splice(index, 1);
stats.size--;
}
return deleted;
};
memoized.has = function(...args) {
const key = keyResolver(...args);
return cache.has(key);
};
memoized.getStats = function() {
return { ...stats };
};
memoized.getCache = function() {
return new Map(cache);
};
return memoized;
}
// 使用示例
function expensiveCalculation(n) {
console.log('Calculating...', n);
let result = 0;
for (let i = 0; i < n * 1000000; i++) {
result += Math.sqrt(i);
}
return result;
}
const memoizedCalculation = memoize(expensiveCalculation, {
maxSize: 100,
ttl: 60000 // 1分钟缓存
});
// 第一次调用会计算
console.log(memoizedCalculation(10));
// 第二次调用直接从缓存读取
console.log(memoizedCalculation(10));
// 查看统计
console.log(memoizedCalculation.getStats());
3.2 异步函数记忆化
javascript
function memoizeAsync(fn, options = {}) {
const {
maxSize = Infinity,
keyResolver = (...args) => JSON.stringify(args),
ttl = null
} = options;
const cache = new Map();
const pendingPromises = new Map();
const lruKeys = [];
const memoized = async function(...args) {
const key = keyResolver(...args);
// 检查缓存
if (cache.has(key)) {
const entry = cache.get(key);
if (ttl && Date.now() - entry.timestamp > ttl) {
cache.delete(key);
const index = lruKeys.indexOf(key);
if (index > -1) lruKeys.splice(index, 1);
} else {
if (maxSize < Infinity) {
const index = lruKeys.indexOf(key);
if (index > -1) {
lruKeys.splice(index, 1);
lruKeys.unshift(key);
}
}
return entry.value;
}
}
// 检查是否已经在执行中
if (pendingPromises.has(key)) {
return pendingPromises.get(key);
}
// 创建新的Promise
const promise = (async () => {
try {
const result = await fn.apply(this, args);
// 缓存结果
cache.set(key, {
value: result,
timestamp: Date.now()
});
if (maxSize < Infinity) {
lruKeys.unshift(key);
if (cache.size > maxSize) {
const lruKey = lruKeys.pop();
cache.delete(lruKey);
}
}
return result;
} finally {
pendingPromises.delete(key);
}
})();
pendingPromises.set(key, promise);
return promise;
};
memoized.clear = () => {
cache.clear();
pendingPromises.clear();
lruKeys.length = 0;
};
memoized.delete = (...args) => {
const key = keyResolver(...args);
const deleted = cache.delete(key);
if (deleted) {
const index = lruKeys.indexOf(key);
if (index > -1) lruKeys.splice(index, 1);
}
pendingPromises.delete(key);
return deleted;
};
memoized.has = (...args) => {
const key = keyResolver(...args);
return cache.has(key);
};
return memoized;
}
// 使用示例
async function fetchUserData(userId) {
console.log('Fetching user data for:', userId);
const response = await fetch(`/api/users/${userId}`);
return response.json();
}
const memoizedFetchUserData = memoizeAsync(fetchUserData, {
ttl: 30000 // 30秒缓存
});
// 多个组件同时请求同一个用户数据
Promise.all([
memoizedFetchUserData(1),
memoizedFetchUserData(1),
memoizedFetchUserData(1)
]).then(results => {
console.log('All results:', results);
// 只会有一次实际的网络请求
});
3.3 React Hook记忆化
javascript
import { useRef, useCallback } from 'react';
function useMemoizedCallback(fn, dependencies = []) {
const cacheRef = useRef(new Map());
const fnRef = useRef(fn);
// 更新函数引用
fnRef.current = fn;
const memoizedFn = useCallback((...args) => {
const cache = cacheRef.current;
const key = JSON.stringify(args);
if (cache.has(key)) {
return cache.get(key);
}
const result = fnRef.current(...args);
cache.set(key, result);
return result;
}, dependencies);
// 清理函数
const clearCache = useCallback(() => {
cacheRef.current.clear();
}, []);
return [memoizedFn, clearCache];
}
// React组件使用示例
function ExpensiveComponent({ data }) {
const [processData, clearCache] = useMemoizedCallback(
(item) => {
// 昂贵的计算
return item.value * Math.sqrt(item.weight);
},
[data]
);
return (
<div>
{data.map(item => (
<div key={item.id}>
{processData(item)}
</div>
))}
<button onClick={clearCache}>Clear Cache</button>
</div>
);
}
四、请求防重和缓存(Deduplication & Caching)
4.1 请求防重系统
javascript
class RequestDeduplicator {
constructor(options = {}) {
this.options = {
defaultTtl: 60000, // 默认缓存时间1分钟
maxCacheSize: 100,
...options
};
this.pendingRequests = new Map();
this.cache = new Map();
this.cacheTimestamps = new Map();
this.stats = {
duplicatesPrevented: 0,
cacheHits: 0,
cacheMisses: 0
};
}
generateKey(config) {
// 生成请求的唯一标识符
const { url, method = 'GET', params = {}, data = {} } = config;
const keyParts = [
method.toUpperCase(),
url,
JSON.stringify(params),
JSON.stringify(data)
];
return keyParts.join('|');
}
async request(config) {
const key = this.generateKey(config);
const now = Date.now();
// 检查缓存
if (this.cache.has(key)) {
const { data, timestamp } = this.cache.get(key);
const ttl = config.ttl || this.options.defaultTtl;
if (now - timestamp < ttl) {
this.stats.cacheHits++;
return Promise.resolve(data);
} else {
// 缓存过期
this.cache.delete(key);
this.cacheTimestamps.delete(key);
}
}
this.stats.cacheMisses++;
// 检查是否有相同的请求正在进行中
if (this.pendingRequests.has(key)) {
this.stats.duplicatesPrevented++;
return this.pendingRequests.get(key);
}
// 创建新的请求
const requestPromise = this.executeRequest(config);
// 存储进行中的请求
this.pendingRequests.set(key, requestPromise);
try {
const result = await requestPromise;
// 缓存成功的结果
if (config.cache !== false) {
this.cache.set(key, {
data: result,
timestamp: now
});
this.cacheTimestamps.set(key, now);
// 清理过期的缓存
this.cleanupCache();
}
return result;
} finally {
// 移除进行中的请求
this.pendingRequests.delete(key);
}
}
async executeRequest(config) {
const { url, method = 'GET', params = {}, data = {}, headers = {} } = config;
// 构建请求URL
let requestUrl = url;
if (params && Object.keys(params).length > 0) {
const queryString = new URLSearchParams(params).toString();
requestUrl += `?${queryString}`;
}
// 发送请求
const response = await fetch(requestUrl, {
method,
headers: {
'Content-Type': 'application/json',
...headers
},
body: method !== 'GET' && method !== 'HEAD' ? JSON.stringify(data) : undefined
});
if (!response.ok) {
throw new Error(`Request failed: ${response.status}`);
}
return response.json();
}
cleanupCache() {
const now = Date.now();
const maxAge = this.options.defaultTtl;
// 清理过期缓存
for (const [key, timestamp] of this.cacheTimestamps.entries()) {
if (now - timestamp > maxAge) {
this.cache.delete(key);
this.cacheTimestamps.delete(key);
}
}
// 清理超出大小的缓存
if (this.cache.size > this.options.maxCacheSize) {
const sortedEntries = Array.from(this.cacheTimestamps.entries())
.sort(([, a], [, b]) => a - b);
const entriesToRemove = sortedEntries.slice(
0,
this.cache.size - this.options.maxCacheSize
);
entriesToRemove.forEach(([key]) => {
this.cache.delete(key);
this.cacheTimestamps.delete(key);
});
}
}
// 手动清理缓存
clearCache(urlPattern) {
if (urlPattern) {
for (const key of this.cache.keys()) {
if (key.includes(urlPattern)) {
this.cache.delete(key);
this.cacheTimestamps.delete(key);
}
}
} else {
this.cache.clear();
this.cacheTimestamps.clear();
}
}
// 预加载数据
prefetch(config) {
const key = this.generateKey(config);
if (!this.cache.has(key) && !this.pendingRequests.has(key)) {
this.request(config).catch(() => {
// 静默失败,预加载不影响主流程
});
}
}
getStats() {
return {
...this.stats,
pendingRequests: this.pendingRequests.size,
cachedResponses: this.cache.size
};
}
}
// 使用示例
const deduplicator = new RequestDeduplicator({
defaultTtl: 30000, // 30秒
maxCacheSize: 50
});
// 多个组件同时请求相同的数据
async function fetchUserProfile(userId) {
const config = {
url: `/api/users/${userId}`,
method: 'GET',
ttl: 60000 // 此请求特定缓存时间
};
return deduplicator.request(config);
}
// 在多个地方同时调用
Promise.all([
fetchUserProfile(1),
fetchUserProfile(1),
fetchUserProfile(1)
]).then(results => {
console.log('Results:', results);
console.log('Stats:', deduplicator.getStats());
});
// 预加载
deduplicator.prefetch({
url: '/api/products',
params: { page: 1, limit: 20 }
});
4.2 分层缓存系统
javascript
class LayeredCache {
constructor(options = {}) {
this.layers = [];
this.options = {
defaultTtl: 300000, // 5分钟
...options
};
this.stats = {
layerHits: {},
totalHits: 0,
totalMisses: 0
};
}
addLayer(layer) {
if (!layer.get || !layer.set || !layer.delete || !layer.clear) {
throw new Error('Cache layer must implement get, set, delete, and clear methods');
}
this.layers.push(layer);
this.stats.layerHits[layer.name || `layer_${this.layers.length}`] = 0;
}
async get(key) {
this.stats.totalHits++;
// 从上层开始查找
for (let i = 0; i < this.layers.length; i++) {
const layer = this.layers[i];
const layerName = layer.name || `layer_${i + 1}`;
try {
const value = await layer.get(key);
if (value !== undefined && value !== null) {
// 命中,更新统计
this.stats.layerHits[layerName] = (this.stats.layerHits[layerName] || 0) + 1;
// 将数据复制到上层缓存(提升)
for (let j = 0; j < i; j++) {
this.layers[j].set(key, value, this.options.defaultTtl).catch(() => {});
}
return value;
}
} catch (error) {
console.warn(`Cache layer ${layerName} error:`, error);
// 继续尝试下一层
}
}
this.stats.totalMisses++;
return null;
}
async set(key, value, ttl = this.options.defaultTtl) {
// 设置所有层级的缓存
const promises = this.layers.map(layer =>
layer.set(key, value, ttl).catch(() => {})
);
await Promise.all(promises);
return value;
}
async delete(key) {
// 删除所有层级的缓存
const promises = this.layers.map(layer =>
layer.delete(key).catch(() => {})
);
await Promise.all(promises);
}
async clear() {
const promises = this.layers.map(layer =>
layer.clear().catch(() => {})
);
await Promise.all(promises);
}
getStats() {
return {
...this.stats,
hitRate: this.stats.totalHits > 0
? (this.stats.totalHits - this.stats.totalMisses) / this.stats.totalHits
: 0
};
}
}
// 内存缓存层实现
class MemoryCacheLayer {
constructor(name = 'memory') {
this.name = name;
this.cache = new Map();
this.timestamps = new Map();
}
async get(key) {
const value = this.cache.get(key);
if (value === undefined) return null;
const timestamp = this.timestamps.get(key);
if (timestamp && Date.now() > timestamp) {
// 已过期
this.cache.delete(key);
this.timestamps.delete(key);
return null;
}
return value;
}
async set(key, value, ttl) {
this.cache.set(key, value);
if (ttl) {
this.timestamps.set(key, Date.now() + ttl);
}
return value;
}
async delete(key) {
this.cache.delete(key);
this.timestamps.delete(key);
}
async clear() {
this.cache.clear();
this.timestamps.clear();
}
}
// IndexedDB缓存层实现
class IndexedDBCacheLayer {
constructor(name = 'indexeddb', dbName = 'cache_db', storeName = 'cache_store') {
this.name = name;
this.dbName = dbName;
this.storeName = storeName;
this.db = null;
this.initDB();
}
async initDB() {
return new Promise((resolve, reject) => {
const request = indexedDB.open(this.dbName, 1);
request.onupgradeneeded = (event) => {
const db = event.target.result;
if (!db.objectStoreNames.contains(this.storeName)) {
db.createObjectStore(this.storeName);
}
};
request.onsuccess = (event) => {
this.db = event.target.result;
resolve();
};
request.onerror = (event) => {
reject(event.target.error);
};
});
}
async get(key) {
if (!this.db) await this.initDB();
return new Promise((resolve, reject) => {
const transaction = this.db.transaction([this.storeName], 'readonly');
const store = transaction.objectStore(this.storeName);
const request = store.get(key);
request.onsuccess = () => {
const result = request.result;
if (result && result.expires && Date.now() > result.expires) {
// 已过期,删除
this.delete(key);
resolve(null);
} else {
resolve(result ? result.value : null);
}
};
request.onerror = () => reject(request.error);
});
}
async set(key, value, ttl) {
if (!this.db) await this.initDB();
return new Promise((resolve, reject) => {
const transaction = this.db.transaction([this.storeName], 'readwrite');
const store = transaction.objectStore(this.storeName);
const item = {
value,
expires: ttl ? Date.now() + ttl : null,
timestamp: Date.now()
};
const request = store.put(item, key);
request.onsuccess = () => resolve(value);
request.onerror = () => reject(request.error);
});
}
async delete(key) {
if (!this.db) await this.initDB();
return new Promise((resolve, reject) => {
const transaction = this.db.transaction([this.storeName], 'readwrite');
const store = transaction.objectStore(this.storeName);
const request = store.delete(key);
request.onsuccess = () => resolve();
request.onerror = () => reject(request.error);
});
}
async clear() {
if (!this.db) await this.initDB();
return new Promise((resolve, reject) => {
const transaction = this.db.transaction([this.storeName], 'readwrite');
const store = transaction.objectStore(this.storeName);
const request = store.clear();
request.onsuccess = () => resolve();
request.onerror = () => reject(request.error);
});
}
}
// 使用示例
const cache = new LayeredCache({
defaultTtl: 300000 // 5分钟
});
// 添加内存缓存层(快速)
cache.addLayer(new MemoryCacheLayer('memory'));
// 添加IndexedDB缓存层(持久化)
cache.addLayer(new IndexedDBCacheLayer('indexeddb'));
// 使用缓存
async function getCachedData(key) {
let data = await cache.get(key);
if (!data) {
// 从网络获取数据
data = await fetchDataFromNetwork();
await cache.set(key, data);
}
return data;
}
五、时间切片(Time Slicing)
5.1 基于requestIdleCallback的实现
javascript
class TimeSlicer {
constructor(options = {}) {
this.options = {
timeout: 1000, // 超时时间
taskChunkSize: 100, // 每个时间片处理的任务数
...options
};
this.tasks = [];
this.isProcessing = false;
this.currentIndex = 0;
this.deferred = null;
this.stats = {
tasksProcessed: 0,
timeSlicesUsed: 0,
totalTime: 0
};
}
addTask(task) {
if (typeof task !== 'function') {
throw new Error('Task must be a function');
}
this.tasks.push(task);
return this;
}
addTasks(tasks) {
tasks.forEach(task => this.addTask(task));
return this;
}
process() {
if (this.isProcessing) {
return Promise.reject(new Error('Already processing'));
}
if (this.tasks.length === 0) {
return Promise.resolve();
}
this.isProcessing = true;
this.currentIndex = 0;
this.stats.tasksProcessed = 0;
this.stats.timeSlicesUsed = 0;
this.stats.totalTime = 0;
return new Promise((resolve, reject) => {
this.deferred = { resolve, reject };
this.processNextChunk();
});
}
processNextChunk() {
const startTime = performance.now();
// 处理一个时间片的任务
for (let i = 0; i < this.options.taskChunkSize; i++) {
if (this.currentIndex >= this.tasks.length) {
this.finishProcessing();
return;
}
try {
const task = this.tasks[this.currentIndex];
task();
this.currentIndex++;
this.stats.tasksProcessed++;
} catch (error) {
this.handleError(error);
return;
}
}
const endTime = performance.now();
this.stats.totalTime += endTime - startTime;
// 检查是否还有任务
if (this.currentIndex < this.tasks.length) {
this.stats.timeSlicesUsed++;
// 使用requestIdleCallback安排下一个时间片
if ('requestIdleCallback' in window) {
requestIdleCallback(
() => this.processNextChunk(),
{ timeout: this.options.timeout }
);
} else {
// 降级方案:使用setTimeout
setTimeout(() => this.processNextChunk(), 0);
}
} else {
this.finishProcessing();
}
}
finishProcessing() {
this.isProcessing = false;
this.deferred.resolve({
tasksProcessed: this.stats.tasksProcessed,
timeSlicesUsed: this.stats.timeSlicesUsed,
totalTime: this.stats.totalTime
});
this.deferred = null;
}
handleError(error) {
this.isProcessing = false;
this.deferred.reject(error);
this.deferred = null;
}
clear() {
this.tasks = [];
this.isProcessing = false;
this.currentIndex = 0;
}
getStats() {
return { ...this.stats };
}
}
// 使用示例
function createExpensiveTask(id) {
return () => {
console.log(`Processing task ${id}`);
// 模拟耗时操作
let sum = 0;
for (let i = 0; i < 1000000; i++) {
sum += Math.sqrt(i);
}
return sum;
};
}
// 创建时间切片处理器
const slicer = new TimeSlicer({
taskChunkSize: 50, // 每个时间片处理50个任务
timeout: 2000 // 2秒超时
});
// 添加大量任务
for (let i = 0; i < 1000; i++) {
slicer.addTask(createExpensiveTask(i));
}
// 开始处理
slicer.process().then(stats => {
console.log('Processing completed:', stats);
}).catch(error => {
console.error('Processing failed:', error);
});
// 可以在处理过程中添加新任务
setTimeout(() => {
slicer.addTask(() => console.log('New task added during processing'));
}, 1000);
5.2 基于Generator的时间切片
javascript
function* taskGenerator(tasks) {
for (let i = 0; i < tasks.length; i++) {
yield tasks[i];
}
}
class GeneratorTimeSlicer {
constructor(options = {}) {
this.options = {
timePerSlice: 16, // 每个时间片16ms(大约一帧的时间)
...options
};
this.taskGenerator = null;
this.isProcessing = false;
this.stats = {
tasksProcessed: 0,
slicesUsed: 0,
totalTime: 0
};
}
processTasks(tasks) {
if (this.isProcessing) {
return Promise.reject(new Error('Already processing'));
}
this.isProcessing = true;
this.taskGenerator = taskGenerator(tasks);
this.stats = { tasksProcessed: 0, slicesUsed: 0, totalTime: 0 };
return new Promise((resolve, reject) => {
this.processNextSlice(resolve, reject);
});
}
processNextSlice(resolve, reject) {
if (!this.isProcessing) return;
const sliceStart = performance.now();
let taskResult;
// 处理一个时间片
while (true) {
const { value: task, done } = this.taskGenerator.next();
if (done) {
this.isProcessing = false;
resolve({
...this.stats,
completed: true
});
return;
}
try {
taskResult = task();
this.stats.tasksProcessed++;
} catch (error) {
this.isProcessing = false;
reject(error);
return;
}
// 检查是否超过时间片限制
if (performance.now() - sliceStart >= this.options.timePerSlice) {
break;
}
}
this.stats.slicesUsed++;
this.stats.totalTime += performance.now() - sliceStart;
// 安排下一个时间片
if ('requestAnimationFrame' in window) {
requestAnimationFrame(() => this.processNextSlice(resolve, reject));
} else {
setTimeout(() => this.processNextSlice(resolve, reject), 0);
}
}
stop() {
this.isProcessing = false;
}
}
// 使用示例:处理大型数组
function processLargeArray(array, processItem, chunkSize = 100) {
return new Promise((resolve) => {
let index = 0;
const results = [];
function processChunk() {
const chunkStart = performance.now();
while (index < array.length) {
results.push(processItem(array[index]));
index++;
// 检查是否处理了足够多的项目或时间到了
if (index % chunkSize === 0 ||
performance.now() - chunkStart > 16) {
break;
}
}
if (index < array.length) {
// 还有更多项目,安排下一个时间片
requestAnimationFrame(processChunk);
} else {
// 完成
resolve(results);
}
}
// 开始处理
requestAnimationFrame(processChunk);
});
}
// 处理10万个项目
const largeArray = Array.from({ length: 100000 }, (_, i) => i);
processLargeArray(largeArray, (item) => {
// 对每个项目进行一些处理
return item * Math.sqrt(item);
}, 1000).then(results => {
console.log(`Processed ${results.length} items`);
});
六、函数节流与防抖(Throttle & Debounce)
6.1 高级节流与防抖实现
javascript
class AdvancedThrottleDebounce {
// 节流:确保函数在一定时间内只执行一次
static throttle(func, wait, options = {}) {
let timeout = null;
let previous = 0;
let result;
let context;
let args;
const { leading = true, trailing = true } = options;
const later = () => {
previous = !leading ? 0 : Date.now();
timeout = null;
if (trailing && args) {
result = func.apply(context, args);
context = args = null;
}
};
const throttled = function(...params) {
const now = Date.now();
if (!previous && !leading) {
previous = now;
}
const remaining = wait - (now - previous);
context = this;
args = params;
if (remaining <= 0 || remaining > wait) {
if (timeout) {
clearTimeout(timeout);
timeout = null;
}
previous = now;
result = func.apply(context, args);
context = args = null;
} else if (!timeout && trailing) {
timeout = setTimeout(later, remaining);
}
return result;
};
throttled.cancel = () => {
clearTimeout(timeout);
previous = 0;
timeout = context = args = null;
};
return throttled;
}
// 防抖:确保函数在最后一次调用后一定时间才执行
static debounce(func, wait, options = {}) {
let timeout = null;
let result;
let context;
let args;
let lastCallTime;
let lastInvokeTime = 0;
const {
leading = false,
trailing = true,
maxWait
} = options;
const invokeFunc = (time) => {
lastInvokeTime = time;
result = func.apply(context, args);
context = args = null;
return result;
};
const leadingEdge = (time) => {
lastInvokeTime = time;
if (trailing) {
timeout = setTimeout(timerExpired, wait);
}
return leading ? invokeFunc(time) : result;
};
const remainingWait = (time) => {
const timeSinceLastCall = time - lastCallTime;
const timeSinceLastInvoke = time - lastInvokeTime;
const timeWaiting = wait - timeSinceLastCall;
return maxWait === undefined
? timeWaiting
: Math.min(timeWaiting, maxWait - timeSinceLastInvoke);
};
const shouldInvoke = (time) => {
const timeSinceLastCall = time - lastCallTime;
const timeSinceLastInvoke = time - lastInvokeTime;
return (
lastCallTime === undefined ||
timeSinceLastCall >= wait ||
timeSinceLastCall < 0 ||
(maxWait !== undefined && timeSinceLastInvoke >= maxWait)
);
};
const timerExpired = () => {
const time = Date.now();
if (shouldInvoke(time)) {
return trailingEdge(time);
}
timeout = setTimeout(timerExpired, remainingWait(time));
};
const trailingEdge = (time) => {
timeout = null;
if (trailing && args) {
return invokeFunc(time);
}
context = args = null;
return result;
};
const debounced = function(...params) {
const time = Date.now();
const isInvoking = shouldInvoke(time);
context = this;
args = params;
lastCallTime = time;
if (isInvoking) {
if (!timeout && leading) {
return leadingEdge(lastCallTime);
}
if (maxWait !== undefined) {
timeout = setTimeout(timerExpired, wait);
return invokeFunc(lastCallTime);
}
}
if (!timeout) {
timeout = setTimeout(timerExpired, wait);
}
return result;
};
debounced.cancel = () => {
if (timeout !== null) {
clearTimeout(timeout);
}
lastInvokeTime = 0;
lastCallTime = 0;
timeout = context = args = null;
};
debounced.flush = () => {
return timeout ? trailingEdge(Date.now()) : result;
};
return debounced;
}
// 立即执行的防抖(第一次立即执行,然后防抖)
static immediateDebounce(func, wait) {
let timeout;
let immediate = true;
return function(...args) {
const context = this;
if (immediate) {
func.apply(context, args);
immediate = false;
}
clearTimeout(timeout);
timeout = setTimeout(() => {
immediate = true;
}, wait);
};
}
// 节流+防抖组合
static throttleDebounce(func, wait, options = {}) {
const {
throttleWait = wait,
debounceWait = wait,
leading = true,
trailing = true
} = options;
const throttled = this.throttle(func, throttleWait, { leading, trailing });
const debounced = this.debounce(func, debounceWait, { leading, trailing });
let lastCall = 0;
return function(...args) {
const now = Date.now();
const timeSinceLastCall = now - lastCall;
lastCall = now;
// 如果距离上次调用时间很短,使用防抖
if (timeSinceLastCall < throttleWait) {
return debounced.apply(this, args);
}
// 否则使用节流
return throttled.apply(this, args);
};
}
}
// 使用示例
// 节流示例:滚动事件
window.addEventListener('scroll', AdvancedThrottleDebounce.throttle(
function() {
console.log('Scroll position:', window.scrollY);
},
100,
{ leading: true, trailing: true }
));
// 防抖示例:搜索输入
const searchInput = document.getElementById('search');
searchInput.addEventListener('input', AdvancedThrottleDebounce.debounce(
function(event) {
console.log('Searching for:', event.target.value);
// 实际搜索逻辑
},
300,
{ leading: false, trailing: true }
));
// 立即执行的防抖示例:按钮点击
const submitButton = document.getElementById('submit');
submitButton.addEventListener('click', AdvancedThrottleDebounce.immediateDebounce(
function() {
console.log('Submit clicked');
// 提交逻辑
},
1000
));
// 组合示例:调整窗口大小
window.addEventListener('resize', AdvancedThrottleDebounce.throttleDebounce(
function() {
console.log('Window resized');
// 调整布局逻辑
},
200,
{ throttleWait: 100, debounceWait: 300 }
));
6.2 React Hook版本的节流防抖
javascript
import { useRef, useCallback, useEffect } from 'react';
// 使用Hook实现节流
function useThrottle(callback, delay, options = {}) {
const { leading = true, trailing = true } = options;
const lastCallTime = useRef(0);
const timeout = useRef(null);
const lastArgs = useRef(null);
const lastThis = useRef(null);
const throttled = useCallback(function(...args) {
const now = Date.now();
lastArgs.current = args;
lastThis.current = this;
if (!leading && !lastCallTime.current) {
lastCallTime.current = now;
}
const remaining = delay - (now - lastCallTime.current);
if (remaining <= 0 || remaining > delay) {
if (timeout.current) {
clearTimeout(timeout.current);
timeout.current = null;
}
lastCallTime.current = now;
callback.apply(this, args);
lastArgs.current = lastThis.current = null;
} else if (!timeout.current && trailing) {
timeout.current = setTimeout(() => {
lastCallTime.current = leading ? Date.now() : 0;
timeout.current = null;
if (trailing && lastArgs.current) {
callback.apply(lastThis.current, lastArgs.current);
lastArgs.current = lastThis.current = null;
}
}, remaining);
}
}, [callback, delay, leading, trailing]);
// 清理函数
useEffect(() => {
return () => {
if (timeout.current) {
clearTimeout(timeout.current);
}
};
}, []);
return throttled;
}
// 使用Hook实现防抖
function useDebounce(callback, delay, options = {}) {
const { leading = false, maxWait } = options;
const lastCallTime = useRef(0);
const lastInvokeTime = useRef(0);
const timeout = useRef(null);
const lastArgs = useRef(null);
const lastThis = useRef(null);
const debounced = useCallback(function(...args) {
const now = Date.now();
lastArgs.current = args;
lastThis.current = this;
lastCallTime.current = now;
const invokeFunc = () => {
lastInvokeTime.current = now;
callback.apply(this, args);
lastArgs.current = lastThis.current = null;
};
const shouldInvoke = () => {
const timeSinceLastCall = now - lastCallTime.current;
const timeSinceLastInvoke = now - lastInvokeTime.current;
return (
lastCallTime.current === 0 ||
timeSinceLastCall >= delay ||
timeSinceLastCall < 0 ||
(maxWait !== undefined && timeSinceLastInvoke >= maxWait)
);
};
if (shouldInvoke()) {
if (!timeout.current && leading) {
invokeFunc();
}
if (maxWait !== undefined) {
timeout.current = setTimeout(() => {
if (timeout.current) {
clearTimeout(timeout.current);
timeout.current = null;
}
invokeFunc();
}, delay);
return;
}
}
if (!timeout.current) {
timeout.current = setTimeout(() => {
timeout.current = null;
if (lastArgs.current) {
invokeFunc();
}
}, delay);
}
}, [callback, delay, leading, maxWait]);
// 清理函数
useEffect(() => {
return () => {
if (timeout.current) {
clearTimeout(timeout.current);
}
};
}, []);
return debounced;
}
// React组件使用示例
function SearchComponent() {
const [query, setQuery] = useState('');
const [results, setResults] = useState([]);
// 防抖搜索函数
const debouncedSearch = useDebounce(async (searchQuery) => {
if (!searchQuery.trim()) {
setResults([]);
return;
}
const response = await fetch(`/api/search?q=${encodeURIComponent(searchQuery)}`);
const data = await response.json();
setResults(data);
}, 300);
const handleInputChange = useCallback((event) => {
const value = event.target.value;
setQuery(value);
debouncedSearch(value);
}, [debouncedSearch]);
return (
<div>
<input
type="text"
value={query}
onChange={handleInputChange}
placeholder="Search..."
/>
<ul>
{results.map(result => (
<li key={result.id}>{result.name}</li>
))}
</ul>
</div>
);
}
七、并发控制与请求池(Concurrency Control & Request Pool)
7.1 智能请求池实现
javascript
class RequestPool {
constructor(options = {}) {
this.options = {
maxConcurrent: 6, // 最大并发数(浏览器限制通常是6)
retryCount: 2, // 重试次数
retryDelay: 1000, // 重试延迟
timeout: 30000, // 超时时间
priority: false, // 是否启用优先级
...options
};
this.queue = [];
this.activeRequests = new Map();
this.requestCount = 0;
this.stats = {
total: 0,
success: 0,
failed: 0,
retried: 0,
queued: 0,
active: 0
};
this.paused = false;
}
addRequest(request, priority = 0) {
const requestId = `req_${Date.now()}_${++this.requestCount}`;
const requestConfig = {
id: requestId,
request,
priority,
retries: 0,
addedAt: Date.now()
};
if (this.options.priority) {
// 按优先级插入队列
let insertIndex = this.queue.length;
for (let i = 0; i < this.queue.length; i++) {
if (priority > this.queue[i].priority) {
insertIndex = i;
break;
}
}
this.queue.splice(insertIndex, 0, requestConfig);
} else {
this.queue.push(requestConfig);
}
this.stats.queued++;
this.stats.total++;
this.processQueue();
return requestId;
}
async processQueue() {
if (this.paused || this.queue.length === 0) return;
// 检查可用并发数
const availableSlots = this.options.maxConcurrent - this.activeRequests.size;
if (availableSlots <= 0) return;
// 获取要处理的任务
const tasksToProcess = this.queue.splice(0, availableSlots);
tasksToProcess.forEach(task => {
this.executeRequest(task);
});
}
async executeRequest(task) {
const { id, request, priority, retries } = task;
this.activeRequests.set(id, task);
this.stats.queued--;
this.stats.active++;
try {
const controller = new AbortController();
const timeoutId = setTimeout(() => {
controller.abort();
}, this.options.timeout);
// 执行请求
const result = await request(controller.signal);
clearTimeout(timeoutId);
// 请求成功
this.activeRequests.delete(id);
this.stats.active--;
this.stats.success++;
// 触发成功事件
this.emit('success', { id, result, retries });
// 继续处理队列
this.processQueue();
return result;
} catch (error) {
clearTimeout(timeoutId);
// 检查是否需要重试
const shouldRetry = retries < this.options.retryCount &&
!this.isAbortError(error);
if (shouldRetry) {
this.stats.retried++;
// 延迟后重试
setTimeout(() => {
task.retries++;
this.queue.unshift(task);
this.stats.queued++;
this.processQueue();
}, this.options.retryDelay);
} else {
// 最终失败
this.activeRequests.delete(id);
this.stats.active--;
this.stats.failed++;
// 触发失败事件
this.emit('error', { id, error, retries });
// 继续处理队列
this.processQueue();
}
}
}
isAbortError(error) {
return error.name === 'AbortError' || error.message === 'The user aborted a request.';
}
// 事件系统
eventHandlers = new Map();
on(event, handler) {
if (!this.eventHandlers.has(event)) {
this.eventHandlers.set(event, []);
}
this.eventHandlers.get(event).push(handler);
}
off(event, handler) {
if (this.eventHandlers.has(event)) {
const handlers = this.eventHandlers.get(event);
const index = handlers.indexOf(handler);
if (index > -1) {
handlers.splice(index, 1);
}
}
}
emit(event, data) {
if (this.eventHandlers.has(event)) {
this.eventHandlers.get(event).forEach(handler => {
try {
handler(data);
} catch (error) {
console.error(`Error in event handler for ${event}:`, error);
}
});
}
}
// 控制方法
pause() {
this.paused = true;
}
resume() {
this.paused = false;
this.processQueue();
}
cancelRequest(requestId) {
// 从队列中移除
const queueIndex = this.queue.findIndex(req => req.id === requestId);
if (queueIndex > -1) {
this.queue.splice(queueIndex, 1);
this.stats.queued--;
return true;
}
// 从活动请求中移除(无法真正取消fetch,但可以标记为取消)
if (this.activeRequests.has(requestId)) {
// 在实际应用中,这里应该取消fetch请求
// 需要保存AbortController并在取消时调用abort()
this.activeRequests.delete(requestId);
this.stats.active--;
return true;
}
return false;
}
clearQueue() {
this.queue = [];
this.stats.queued = 0;
}
getStats() {
return {
...this.stats,
queueLength: this.queue.length,
activeRequests: this.activeRequests.size
};
}
waitForAll() {
return new Promise((resolve) => {
const checkInterval = setInterval(() => {
if (this.queue.length === 0 && this.activeRequests.size === 0) {
clearInterval(checkInterval);
resolve(this.stats);
}
}, 100);
});
}
}
// 使用示例
const requestPool = new RequestPool({
maxConcurrent: 4,
retryCount: 2,
timeout: 10000,
priority: true
});
// 添加请求事件监听
requestPool.on('success', ({ id, result }) => {
console.log(`Request ${id} succeeded`);
});
requestPool.on('error', ({ id, error }) => {
console.error(`Request ${id} failed:`, error);
});
// 创建请求函数
function createRequest(url, data = null) {
return async (signal) => {
const options = {
method: data ? 'POST' : 'GET',
headers: {
'Content-Type': 'application/json'
},
signal
};
if (data) {
options.body = JSON.stringify(data);
}
const response = await fetch(url, options);
if (!response.ok) {
throw new Error(`HTTP ${response.status}`);
}
return response.json();
};
}
// 添加多个请求
const urls = [
'/api/users/1',
'/api/users/2',
'/api/users/3',
'/api/products/1',
'/api/products/2',
'/api/orders/1'
];
urls.forEach((url, index) => {
const priority = index < 3 ? 10 : 1; // 前3个请求高优先级
requestPool.addRequest(createRequest(url), priority);
});
// 等待所有请求完成
requestPool.waitForAll().then(stats => {
console.log('All requests completed:', stats);
});
// 动态调整并发数
setTimeout(() => {
requestPool.options.maxConcurrent = 2;
}, 5000);
7.2 带缓存的请求池
javascript
class CachedRequestPool extends RequestPool {
constructor(options = {}) {
super(options);
this.cache = new Map();
this.cacheOptions = {
ttl: options.cacheTtl || 300000, // 5分钟
maxSize: options.cacheMaxSize || 100,
...options.cacheOptions
};
this.cacheHits = 0;
this.cacheMisses = 0;
}
addRequest(request, priority = 0, cacheKey = null) {
// 生成缓存键
const actualCacheKey = cacheKey || this.generateCacheKey(request);
// 检查缓存
if (this.cache.has(actualCacheKey)) {
const cached = this.cache.get(actualCacheKey);
if (Date.now() - cached.timestamp < this.cacheOptions.ttl) {
this.cacheHits++;
// 立即返回缓存结果
return Promise.resolve(cached.data);
} else {
// 缓存过期
this.cache.delete(actualCacheKey);
}
}
this.cacheMisses++;
// 创建包装的请求函数
const wrappedRequest = async (signal) => {
try {
const result = await request(signal);
// 缓存结果
this.cache.set(actualCacheKey, {
data: result,
timestamp: Date.now()
});
// 清理过期缓存
this.cleanupCache();
return result;
} catch (error) {
throw error;
}
};
// 添加到父类队列
return new Promise((resolve, reject) => {
const requestId = super.addRequest(wrappedRequest, priority);
// 监听完成事件
const successHandler = ({ id, result }) => {
if (id === requestId) {
resolve(result);
this.off('success', successHandler);
this.off('error', errorHandler);
}
};
const errorHandler = ({ id, error }) => {
if (id === requestId) {
reject(error);
this.off('success', successHandler);
this.off('error', errorHandler);
}
};
this.on('success', successHandler);
this.on('error', errorHandler);
});
}
generateCacheKey(request) {
// 根据请求函数生成缓存键
// 这是一个简单实现,实际应用中可能需要更复杂的逻辑
return `cache_${Date.now()}_${Math.random().toString(36).substr(2)}`;
}
cleanupCache() {
const now = Date.now();
const maxAge = this.cacheOptions.ttl;
// 清理过期缓存
for (const [key, value] of this.cache.entries()) {
if (now - value.timestamp > maxAge) {
this.cache.delete(key);
}
}
// 清理超出大小的缓存
if (this.cache.size > this.cacheOptions.maxSize) {
const entries = Array.from(this.cache.entries());
entries.sort(([, a], [, b]) => a.timestamp - b.timestamp);
const toRemove = entries.slice(0, this.cache.size - this.cacheOptions.maxSize);
toRemove.forEach(([key]) => this.cache.delete(key));
}
}
clearCache() {
this.cache.clear();
this.cacheHits = 0;
this.cacheMisses = 0;
}
getCacheStats() {
return {
hits: this.cacheHits,
misses: this.cacheMisses,
hitRate: this.cacheHits + this.cacheMisses > 0
? this.cacheHits / (this.cacheHits + this.cacheMisses)
: 0,
size: this.cache.size
};
}
}
八、数据分批处理(Batch Processing)
8.1 智能数据批处理器
javascript
class BatchProcessor {
constructor(options = {}) {
this.options = {
batchSize: 100,
delay: 100, // 延迟时间(毫秒)
maxBatches: 10, // 最大批次数
autoProcess: true, // 是否自动处理
...options
};
this.batch = [];
this.batchQueue = [];
this.isProcessing = false;
this.timer = null;
this.stats = {
totalItems: 0,
processedItems: 0,
batchesProcessed: 0,
processingTime: 0
};
if (this.options.autoProcess) {
this.startAutoProcess();
}
}
add(item) {
this.batch.push(item);
this.stats.totalItems++;
if (this.batch.length >= this.options.batchSize) {
this.enqueueBatch();
} else if (this.options.autoProcess && !this.timer) {
this.startTimer();
}
return this;
}
addMany(items) {
items.forEach(item => this.add(item));
return this;
}
enqueueBatch() {
if (this.batch.length === 0) return;
const batchToEnqueue = [...this.batch];
this.batch = [];
this.batchQueue.push(batchToEnqueue);
// 检查队列长度
if (this.batchQueue.length > this.options.maxBatches) {
console.warn('Batch queue is full, consider increasing maxBatches or batchSize');
}
if (this.timer) {
clearTimeout(this.timer);
this.timer = null;
}
if (this.options.autoProcess && !this.isProcessing) {
this.processQueue();
}
}
startTimer() {
if (this.timer) {
clearTimeout(this.timer);
}
this.timer = setTimeout(() => {
this.enqueueBatch();
this.timer = null;
}, this.options.delay);
}
async processQueue() {
if (this.isProcessing || this.batchQueue.length === 0) return;
this.isProcessing = true;
while (this.batchQueue.length > 0) {
const batch = this.batchQueue.shift();
try {
const startTime = Date.now();
await this.processBatch(batch);
const endTime = Date.now();
this.stats.processedItems += batch.length;
this.stats.batchesProcessed++;
this.stats.processingTime += endTime - startTime;
// 触发批次完成事件
this.emit('batchComplete', {
batch,
size: batch.length,
processingTime: endTime - startTime
});
} catch (error) {
console.error('Batch processing error:', error);
// 触发错误事件
this.emit('error', {
error,
batch,
size: batch.length
});
}
}
this.isProcessing = false;
// 触发队列完成事件
this.emit('queueEmpty', this.stats);
}
async processBatch(batch) {
// 这是一个抽象方法,需要在子类中实现
throw new Error('processBatch method must be implemented');
}
flush() {
// 强制处理当前批次
if (this.batch.length > 0) {
this.enqueueBatch();
}
return this.processQueue();
}
waitForCompletion() {
return new Promise((resolve) => {
const checkComplete = () => {
if (this.batch.length === 0 &&
this.batchQueue.length === 0 &&
!this.isProcessing) {
resolve(this.stats);
} else {
setTimeout(checkComplete, 50);
}
};
checkComplete();
});
}
// 事件系统
eventHandlers = new Map();
on(event, handler) {
if (!this.eventHandlers.has(event)) {
this.eventHandlers.set(event, []);
}
this.eventHandlers.get(event).push(handler);
}
off(event, handler) {
if (this.eventHandlers.has(event)) {
const handlers = this.eventHandlers.get(event);
const index = handlers.indexOf(handler);
if (index > -1) {
handlers.splice(index, 1);
}
}
}
emit(event, data) {
if (this.eventHandlers.has(event)) {
this.eventHandlers.get(event).forEach(handler => {
try {
handler(data);
} catch (error) {
console.error(`Error in event handler for ${event}:`, error);
}
});
}
}
getStats() {
return {
...this.stats,
itemsInBatch: this.batch.length,
batchesInQueue: this.batchQueue.length,
isProcessing: this.isProcessing
};
}
reset() {
this.batch = [];
this.batchQueue = [];
this.isProcessing = false;
if (this.timer) {
clearTimeout(this.timer);
this.timer = null;
}
this.stats = {
totalItems: 0,
processedItems: 0,
batchesProcessed: 0,
processingTime: 0
};
}
}
// 使用示例:API批量请求处理器
class ApiBatchProcessor extends BatchProcessor {
constructor(apiEndpoint, options = {}) {
super({
batchSize: 50,
delay: 500,
...options
});
this.apiEndpoint = apiEndpoint;
}
async processBatch(batch) {
// 批量发送请求
const response = await fetch(this.apiEndpoint, {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({ items: batch })
});
if (!response.ok) {
throw new Error(`API request failed: ${response.status}`);
}
return response.json();
}
}
// 使用示例
const processor = new ApiBatchProcessor('/api/batch-save', {
batchSize: 100,
delay: 1000
});
// 监听事件
processor.on('batchComplete', ({ batch, size, processingTime }) => {
console.log(`Processed batch of ${size} items in ${processingTime}ms`);
});
processor.on('queueEmpty', (stats) => {
console.log('All batches processed:', stats);
});
// 添加大量数据
for (let i = 0; i < 1000; i++) {
processor.add({ id: i, data: `Item ${i}` });
}
// 等待处理完成
processor.waitForCompletion().then(stats => {
console.log('Processing completed:', stats);
});
8.2 数据库批量操作
javascript
class DatabaseBatchProcessor extends BatchProcessor {
constructor(dbName, storeName, options = {}) {
super({
batchSize: 100,
delay: 100,
...options
});
this.dbName = dbName;
this.storeName = storeName;
this.db = null;
this.initDatabase();
}
async initDatabase() {
return new Promise((resolve, reject) => {
const request = indexedDB.open(this.dbName, 1);
request.onupgradeneeded = (event) => {
const db = event.target.result;
if (!db.objectStoreNames.contains(this.storeName)) {
db.createObjectStore(this.storeName, { keyPath: 'id' });
}
};
request.onsuccess = (event) => {
this.db = event.target.result;
resolve();
};
request.onerror = (event) => {
reject(event.target.error);
};
});
}
async processBatch(batch) {
if (!this.db) await this.initDatabase();
return new Promise((resolve, reject) => {
const transaction = this.db.transaction([this.storeName], 'readwrite');
const store = transaction.objectStore(this.storeName);
let completed = 0;
let errors = [];
batch.forEach(item => {
const request = store.put(item);
request.onsuccess = () => {
completed++;
if (completed === batch.length) {
if (errors.length > 0) {
reject(new Error(`Failed to save ${errors.length} items`));
} else {
resolve({ saved: batch.length });
}
}
};
request.onerror = (event) => {
errors.push({ item, error: event.target.error });
completed++;
if (completed === batch.length) {
if (errors.length > 0) {
reject(new Error(`Failed to save ${errors.length} items`));
} else {
resolve({ saved: batch.length - errors.length });
}
}
};
});
});
}
async queryBatch(queryFn, batchSize = 100) {
if (!this.db) await this.initDatabase();
return new Promise((resolve, reject) => {
const transaction = this.db.transaction([this.storeName], 'readonly');
const store = transaction.objectStore(this.storeName);
const request = store.openCursor();
const results = [];
let processed = 0;
request.onsuccess = (event) => {
const cursor = event.target.result;
if (cursor) {
// 应用查询函数
if (queryFn(cursor.value)) {
results.push(cursor.value);
}
processed++;
cursor.continue();
// 批量处理
if (processed % batchSize === 0) {
// 短暂暂停以避免阻塞主线程
setTimeout(() => {}, 0);
}
} else {
resolve(results);
}
};
request.onerror = (event) => {
reject(event.target.error);
};
});
}
}
// 使用示例
const dbProcessor = new DatabaseBatchProcessor('myDatabase', 'items');
// 批量添加数据
for (let i = 0; i < 10000; i++) {
dbProcessor.add({
id: i,
name: `Item ${i}`,
value: Math.random(),
timestamp: Date.now()
});
}
// 批量查询
dbProcessor.queryBatch(
item => item.value > 0.5,
500
).then(results => {
console.log(`Found ${results.length} items with value > 0.5`);
});
九、对象池模式(Object Pool Pattern)
9.1 通用对象池实现
javascript
class ObjectPool {
constructor(createFn, options = {}) {
if (typeof createFn !== 'function') {
throw new Error('createFn must be a function');
}
this.createFn = createFn;
this.options = {
maxSize: 100,
minSize: 0,
validate: null, // 验证函数
reset: null, // 重置函数
...options
};
this.pool = [];
this.active = new Set();
this.stats = {
created: 0,
reused: 0,
destroyed: 0,
peakSize: 0
};
// 预创建对象
this.prepopulate();
}
prepopulate() {
const count = Math.min(this.options.minSize, this.options.maxSize);
for (let i = 0; i < count; i++) {
const obj = this.createNew();
this.pool.push(obj);
}
}
createNew() {
const obj = this.createFn();
this.stats.created++;
return obj;
}
acquire() {
let obj;
if (this.pool.length > 0) {
// 从池中获取
obj = this.pool.pop();
// 验证对象
if (this.options.validate && !this.options.validate(obj)) {
// 对象无效,销毁并创建新的
this.destroyObject(obj);
obj = this.createNew();
} else {
this.stats.reused++;
}
} else {
// 池为空,创建新对象
obj = this.createNew();
}
// 重置对象状态
if (this.options.reset) {
this.options.reset(obj);
}
this.active.add(obj);
this.updatePeakSize();
return obj;
}
release(obj) {
if (!this.active.has(obj)) {
console.warn('Object not active in pool');
return;
}
this.active.delete(obj);
// 检查池是否已满
if (this.pool.length < this.options.maxSize) {
// 重置对象
if (this.options.reset) {
this.options.reset(obj);
}
this.pool.push(obj);
} else {
// 池已满,销毁对象
this.destroyObject(obj);
}
}
destroyObject(obj) {
// 调用清理函数(如果存在)
if (obj.destroy && typeof obj.destroy === 'function') {
obj.destroy();
}
this.stats.destroyed++;
}
updatePeakSize() {
const totalSize = this.pool.length + this.active.size;
if (totalSize > this.stats.peakSize) {
this.stats.peakSize = totalSize;
}
}
clear() {
// 销毁所有对象
[...this.pool, ...this.active].forEach(obj => {
this.destroyObject(obj);
});
this.pool = [];
this.active.clear();
}
getStats() {
return {
...this.stats,
poolSize: this.pool.length,
activeSize: this.active.size,
totalSize: this.pool.length + this.active.size
};
}
// 执行函数并自动管理对象
async execute(callback) {
const obj = this.acquire();
try {
const result = await callback(obj);
return result;
} finally {
this.release(obj);
}
}
}
// 使用示例:DOM元素池
class DOMElementPool extends ObjectPool {
constructor(elementType, options = {}) {
super(() => {
const element = document.createElement(elementType);
element.style.display = 'none'; // 初始隐藏
document.body.appendChild(element);
return element;
}, options);
this.elementType = elementType;
}
acquire(styles = {}) {
const element = super.acquire();
// 应用样式
Object.assign(element.style, {
display: '',
...styles
});
return element;
}
release(element) {
// 隐藏元素
element.style.display = 'none';
// 清除内容
element.innerHTML = '';
super.release(element);
}
}
// 使用示例
const divPool = new DOMElementPool('div', {
maxSize: 50,
minSize: 10,
reset: (div) => {
div.className = '';
div.style.cssText = '';
div.textContent = '';
}
});
// 使用对象池创建临时元素
for (let i = 0; i < 1000; i++) {
const div = divPool.acquire({
position: 'absolute',
left: `${Math.random() * 100}%`,
top: `${Math.random() * 100}%`,
width: '50px',
height: '50px',
backgroundColor: `hsl(${Math.random() * 360}, 100%, 50%)`
});
div.textContent = i;
// 模拟使用
setTimeout(() => {
divPool.release(div);
}, Math.random() * 3000);
}
// 查看统计
setTimeout(() => {
console.log('Pool stats:', divPool.getStats());
}, 5000);
9.2 连接池实现
javascript
class ConnectionPool {
constructor(createConnection, options = {}) {
this.createConnection = createConnection;
this.options = {
maxConnections: 10,
minConnections: 2,
idleTimeout: 30000, // 空闲超时时间
acquireTimeout: 5000, // 获取连接超时时间
testOnBorrow: true, // 获取时测试连接
...options
};
this.pool = [];
this.active = new Set();
this.waiting = [];
this.timers = new Map();
this.stats = {
created: 0,
destroyed: 0,
acquired: 0,
released: 0,
timeoutErrors: 0,
connectionErrors: 0
};
// 初始化连接池
this.init();
}
async init() {
for (let i = 0; i < this.options.minConnections; i++) {
await this.createAndAddConnection();
}
}
async createAndAddConnection() {
try {
const connection = await this.createConnection();
this.pool.push({
connection,
lastUsed: Date.now(),
valid: true
});
this.stats.created++;
return connection;
} catch (error) {
this.stats.connectionErrors++;
throw error;
}
}
async acquire() {
this.stats.acquired++;
// 1. 检查空闲连接
for (let i = 0; i < this.pool.length; i++) {
const item = this.pool[i];
if (item.valid) {
// 检查连接是否有效
if (this.options.testOnBorrow) {
try {
await this.testConnection(item.connection);
} catch (error) {
item.valid = false;
continue;
}
}
// 从池中移除
const [acquiredItem] = this.pool.splice(i, 1);
this.active.add(acquiredItem.connection);
// 设置最后使用时间
acquiredItem.lastUsed = Date.now();
return acquiredItem.connection;
}
}
// 2. 检查是否可以创建新连接
const totalConnections = this.pool.length + this.active.size;
if (totalConnections < this.options.maxConnections) {
const connection = await this.createAndAddConnection();
this.active.add(connection);
return connection;
}
// 3. 等待可用连接
return new Promise((resolve, reject) => {
const waitStart = Date.now();
const waitingRequest = {
resolve,
reject,
timer: setTimeout(() => {
// 超时处理
const index = this.waiting.indexOf(waitingRequest);
if (index > -1) {
this.waiting.splice(index, 1);
}
this.stats.timeoutErrors++;
reject(new Error('Connection acquisition timeout'));
}, this.options.acquireTimeout)
};
this.waiting.push(waitingRequest);
// 立即尝试处理等待队列
this.processWaitingQueue();
});
}
release(connection) {
if (!this.active.has(connection)) {
console.warn('Connection not active in pool');
return;
}
this.active.delete(connection);
this.stats.released++;
// 检查连接是否仍然有效
if (this.isConnectionValid(connection)) {
this.pool.push({
connection,
lastUsed: Date.now(),
valid: true
});
// 清理空闲超时的连接
this.cleanupIdleConnections();
// 处理等待队列
this.processWaitingQueue();
} else {
// 连接无效,销毁
this.destroyConnection(connection);
}
}
async testConnection(connection) {
// 默认实现,子类应该覆盖这个方法
return Promise.resolve();
}
isConnectionValid(connection) {
// 默认实现,子类应该覆盖这个方法
return true;
}
destroyConnection(connection) {
// 清理连接资源
if (connection.destroy && typeof connection.destroy === 'function') {
connection.destroy();
} else if (connection.close && typeof connection.close === 'function') {
connection.close();
}
this.stats.destroyed++;
}
cleanupIdleConnections() {
const now = Date.now();
const idleTimeout = this.options.idleTimeout;
for (let i = this.pool.length - 1; i >= 0; i--) {
const item = this.pool[i];
// 检查空闲时间
if (now - item.lastUsed > idleTimeout) {
// 保留最小连接数
if (this.pool.length > this.options.minConnections) {
const [idleItem] = this.pool.splice(i, 1);
this.destroyConnection(idleItem.connection);
}
}
}
}
processWaitingQueue() {
while (this.waiting.length > 0 && this.pool.length > 0) {
const waitingRequest = this.waiting.shift();
clearTimeout(waitingRequest.timer);
// 获取连接
const item = this.pool.pop();
this.active.add(item.connection);
item.lastUsed = Date.now();
waitingRequest.resolve(item.connection);
}
}
async execute(callback) {
const connection = await this.acquire();
try {
const result = await callback(connection);
return result;
} finally {
this.release(connection);
}
}
getStats() {
return {
...this.stats,
poolSize: this.pool.length,
activeSize: this.active.size,
waitingSize: this.waiting.length,
totalSize: this.pool.length + this.active.size
};
}
clear() {
// 清理所有连接
[...this.pool, ...this.active].forEach(item => {
this.destroyConnection(item.connection || item);
});
// 清理等待队列
this.waiting.forEach(request => {
clearTimeout(request.timer);
request.reject(new Error('Pool cleared'));
});
this.pool = [];
this.active.clear();
this.waiting = [];
}
}
// 使用示例:WebSocket连接池
class WebSocketPool extends ConnectionPool {
constructor(url, options = {}) {
super(async () => {
return new Promise((resolve, reject) => {
const ws = new WebSocket(url);
ws.onopen = () => resolve(ws);
ws.onerror = (error) => reject(error);
});
}, options);
this.url = url;
}
async testConnection(ws) {
return new Promise((resolve, reject) => {
if (ws.readyState === WebSocket.OPEN) {
resolve();
} else if (ws.readyState === WebSocket.CONNECTING) {
// 等待连接
const onOpen = () => {
ws.removeEventListener('open', onOpen);
resolve();
};
const onError = () => {
ws.removeEventListener('error', onError);
reject(new Error('WebSocket connection failed'));
};
ws.addEventListener('open', onOpen);
ws.addEventListener('error', onError);
// 超时
setTimeout(() => {
ws.removeEventListener('open', onOpen);
ws.removeEventListener('error', onError);
reject(new Error('WebSocket connection timeout'));
}, 5000);
} else {
reject(new Error('WebSocket is not open'));
}
});
}
isConnectionValid(ws) {
return ws.readyState === WebSocket.OPEN;
}
destroyConnection(ws) {
if (ws.readyState === WebSocket.OPEN || ws.readyState === WebSocket.CONNECTING) {
ws.close();
}
super.destroyConnection(ws);
}
}
// 使用示例
const wsPool = new WebSocketPool('wss://echo.websocket.org', {
maxConnections: 5,
minConnections: 1,
idleTimeout: 60000
});
// 使用连接池发送消息
async function sendMessage(message) {
return wsPool.execute(async (ws) => {
return new Promise((resolve, reject) => {
const messageId = Date.now();
const handler = (event) => {
try {
const data = JSON.parse(event.data);
if (data.id === messageId) {
ws.removeEventListener('message', handler);
resolve(data);
}
} catch (error) {
// 忽略解析错误
}
};
ws.addEventListener('message', handler);
// 设置超时
setTimeout(() => {
ws.removeEventListener('message', handler);
reject(new Error('Message timeout'));
}, 5000);
// 发送消息
ws.send(JSON.stringify({
id: messageId,
message
}));
});
});
}
// 并发发送消息
Promise.all([
sendMessage('Hello 1'),
sendMessage('Hello 2'),
sendMessage('Hello 3'),
sendMessage('Hello 4'),
sendMessage('Hello 5')
]).then(responses => {
console.log('All messages sent:', responses);
});
十、Web Worker优化计算密集型任务
10.1 智能Worker池
javascript
class WorkerPool {
constructor(workerScript, options = {}) {
this.workerScript = workerScript;
this.options = {
maxWorkers: navigator.hardwareConcurrency || 4,
idleTimeout: 30000, // 空闲超时时间
...options
};
this.workers = [];
this.idleWorkers = [];
this.taskQueue = [];
this.taskCallbacks = new Map();
this.workerStates = new Map();
this.stats = {
tasksCompleted: 0,
tasksFailed: 0,
workersCreated: 0,
workersDestroyed: 0
};
// 初始化Worker
this.initWorkers();
}
initWorkers() {
const initialCount = Math.min(2, this.options.maxWorkers);
for (let i = 0; i < initialCount; i++) {
this.createWorker();
}
}
createWorker() {
if (this.workers.length >= this.options.maxWorkers) {
return null;
}
let worker;
if (typeof this.workerScript === 'string') {
worker = new Worker(this.workerScript);
} else if (typeof this.workerScript === 'function') {
// 从函数创建Worker
const workerBlob = new Blob([
`(${this.workerScript.toString()})()`
], { type: 'application/javascript' });
worker = new Worker(URL.createObjectURL(workerBlob));
} else {
throw new Error('workerScript must be a URL string or a function');
}
const workerId = this.workers.length;
worker.id = workerId;
// 设置消息处理
worker.onmessage = (event) => {
this.handleWorkerMessage(workerId, event);
};
worker.onerror = (error) => {
this.handleWorkerError(workerId, error);
};
worker.onmessageerror = (error) => {
this.handleWorkerError(workerId, error);
};
this.workers.push(worker);
this.idleWorkers.push(workerId);
this.workerStates.set(workerId, {
idle: true,
currentTask: null,
lastUsed: Date.now()
});
this.stats.workersCreated++;
return workerId;
}
handleWorkerMessage(workerId, event) {
const state = this.workerStates.get(workerId);
if (!state || !state.currentTask) return;
const taskId = state.currentTask;
const callback = this.taskCallbacks.get(taskId);
if (callback) {
if (event.data.error) {
callback.reject(new Error(event.data.error));
this.stats.tasksFailed++;
} else {
callback.resolve(event.data.result);
this.stats.tasksCompleted++;
}
this.taskCallbacks.delete(taskId);
}
// 标记Worker为空闲
state.idle = true;
state.currentTask = null;
state.lastUsed = Date.now();
this.idleWorkers.push(workerId);
// 处理下一个任务
this.processQueue();
// 清理空闲超时的Worker
this.cleanupIdleWorkers();
}
handleWorkerError(workerId, error) {
console.error(`Worker ${workerId} error:`, error);
const state = this.workerStates.get(workerId);
if (state && state.currentTask) {
const taskId = state.currentTask;
const callback = this.taskCallbacks.get(taskId);
if (callback) {
callback.reject(error);
this.stats.tasksFailed++;
this.taskCallbacks.delete(taskId);
}
}
// 销毁Worker
this.destroyWorker(workerId);
// 创建新的Worker替换
this.createWorker();
// 处理队列中的任务
this.processQueue();
}
destroyWorker(workerId) {
const workerIndex = this.workers.findIndex(w => w.id === workerId);
if (workerIndex === -1) return;
const worker = this.workers[workerIndex];
// 终止Worker
worker.terminate();
// 从数组中移除
this.workers.splice(workerIndex, 1);
// 更新其他Worker的ID
this.workers.forEach((w, index) => {
w.id = index;
});
// 清理状态
this.workerStates.delete(workerId);
// 从空闲列表中移除
const idleIndex = this.idleWorkers.indexOf(workerId);
if (idleIndex > -1) {
this.idleWorkers.splice(idleIndex, 1);
}
this.stats.workersDestroyed++;
}
cleanupIdleWorkers() {
const now = Date.now();
const idleTimeout = this.options.idleTimeout;
// 保留至少一个Worker
while (this.idleWorkers.length > 1) {
const workerId = this.idleWorkers[0];
const state = this.workerStates.get(workerId);
if (state && now - state.lastUsed > idleTimeout) {
this.destroyWorker(workerId);
} else {
break;
}
}
}
processQueue() {
while (this.taskQueue.length > 0 && this.idleWorkers.length > 0) {
const task = this.taskQueue.shift();
const workerId = this.idleWorkers.shift();
this.executeTask(workerId, task);
}
// 如果没有空闲Worker但有任务,考虑创建新Worker
if (this.taskQueue.length > 0 && this.workers.length < this.options.maxWorkers) {
this.createWorker();
this.processQueue();
}
}
executeTask(workerId, task) {
const worker = this.workers.find(w => w.id === workerId);
if (!worker) return;
const state = this.workerStates.get(workerId);
if (!state) return;
state.idle = false;
state.currentTask = task.id;
state.lastUsed = Date.now();
worker.postMessage({
taskId: task.id,
data: task.data,
type: task.type
});
}
runTask(data, type = 'default') {
const taskId = `task_${Date.now()}_${Math.random().toString(36).substr(2)}`;
return new Promise((resolve, reject) => {
const task = {
id: taskId,
data,
type
};
this.taskCallbacks.set(taskId, { resolve, reject });
this.taskQueue.push(task);
this.processQueue();
});
}
// 批量执行任务
runTasks(tasks, type = 'default') {
return Promise.all(
tasks.map(taskData => this.runTask(taskData, type))
);
}
// 执行函数并自动清理
async execute(data, processor) {
if (typeof processor !== 'function') {
throw new Error('Processor must be a function');
}
// 将处理器函数发送到Worker
const taskId = await this.runTask({
data,
processor: processor.toString()
}, 'function');
return taskId;
}
getStats() {
return {
...this.stats,
workers: this.workers.length,
idleWorkers: this.idleWorkers.length,
activeWorkers: this.workers.length - this.idleWorkers.length,
queuedTasks: this.taskQueue.length,
activeTasks: this.taskCallbacks.size
};
}
terminate() {
// 终止所有Worker
this.workers.forEach(worker => {
worker.terminate();
});
// 清理所有任务
this.taskCallbacks.forEach(({ reject }) => {
reject(new Error('Worker pool terminated'));
});
this.workers = [];
this.idleWorkers = [];
this.taskQueue = [];
this.taskCallbacks.clear();
this.workerStates.clear();
}
}
// Worker脚本示例
const workerScript = function() {
// Worker内部代码
self.onmessage = function(event) {
const { taskId, data, type } = event.data;
try {
let result;
switch (type) {
case 'function':
// 执行传入的函数
const { data: taskData, processor } = data;
const func = eval(`(${processor})`);
result = func(taskData);
break;
case 'calculate':
// 计算密集型任务
result = expensiveCalculation(data);
break;
case 'process':
// 数据处理任务
result = processData(data);
break;
default:
result = data;
}
self.postMessage({ taskId, result });
} catch (error) {
self.postMessage({
taskId,
error: error.message || 'Unknown error'
});
}
};
function expensiveCalculation(data) {
let result = 0;
for (let i = 0; i < data.iterations || 1000000; i++) {
result += Math.sqrt(i) * Math.sin(i);
}
return result;
}
function processData(data) {
// 数据处理逻辑
return data.map(item => ({
...item,
processed: true,
timestamp: Date.now()
}));
}
};
// 使用示例
const workerPool = new WorkerPool(workerScript, {
maxWorkers: 4,
idleTimeout: 60000
});
// 执行计算密集型任务
async function runCalculations() {
const tasks = Array.from({ length: 10 }, (_, i) => ({
iterations: 1000000 * (i + 1)
}));
const startTime = Date.now();
const results = await workerPool.runTasks(tasks, 'calculate');
const endTime = Date.now();
console.log(`Calculations completed in ${endTime - startTime}ms`);
console.log('Results:', results);
return results;
}
// 执行函数
async function runCustomFunction() {
const processor = (data) => {
// 这是在Worker中执行的函数
let sum = 0;
for (let i = 0; i < data.length; i++) {
sum += data[i] * Math.sqrt(data[i]);
}
return sum;
};
const data = Array.from({ length: 1000000 }, () => Math.random());
const result = await workerPool.execute(data, processor);
console.log('Custom function result:', result);
}
// 监控统计
setInterval(() => {
console.log('Worker pool stats:', workerPool.getStats());
}, 5000);
10.2 专用Worker优化
javascript
class ImageProcessingWorker {
constructor() {
this.worker = this.createWorker();
this.taskQueue = new Map();
this.nextTaskId = 1;
}
createWorker() {
const workerCode = `
self.onmessage = function(event) {
const { taskId, operation, imageData, params } = event.data;
try {
let result;
switch (operation) {
case 'resize':
result = resizeImage(imageData, params);
break;
case 'filter':
result = applyFilter(imageData, params);
break;
case 'compress':
result = compressImage(imageData, params);
break;
default:
throw new Error('Unknown operation: ' + operation);
}
self.postMessage({ taskId, result }, [result]);
} catch (error) {
self.postMessage({ taskId, error: error.message });
}
};
function resizeImage(imageData, { width, height, quality = 0.9 }) {
// 创建离屏Canvas
const canvas = new OffscreenCanvas(width, height);
const ctx = canvas.getContext('2d');
// 绘制并缩放图像
ctx.drawImage(imageData, 0, 0, width, height);
// 转换为Blob
return canvas.convertToBlob({ quality });
}
function applyFilter(imageData, { filter, intensity = 1 }) {
const canvas = new OffscreenCanvas(
imageData.width,
imageData.height
);
const ctx = canvas.getContext('2d');
ctx.drawImage(imageData, 0, 0);
const imageDataObj = ctx.getImageData(
0, 0,
canvas.width,
canvas.height
);
// 应用滤镜
const data = imageDataObj.data;
for (let i = 0; i < data.length; i += 4) {
// 简单灰度滤镜示例
if (filter === 'grayscale') {
const avg = (data[i] + data[i + 1] + data[i + 2]) / 3;
data[i] = data[i + 1] = data[i + 2] = avg;
}
// 更多滤镜...
}
ctx.putImageData(imageDataObj, 0, 0);
return canvas.convertToBlob();
}
function compressImage(imageData, { quality = 0.7 }) {
const canvas = new OffscreenCanvas(
imageData.width,
imageData.height
);
const ctx = canvas.getContext('2d');
ctx.drawImage(imageData, 0, 0);
return canvas.convertToBlob({ quality });
}
`;
const blob = new Blob([workerCode], { type: 'application/javascript' });
return new Worker(URL.createObjectURL(blob));
}
processImage(imageElement, operation, params = {}) {
return new Promise((resolve, reject) => {
const taskId = this.nextTaskId++;
// 创建Canvas来获取ImageData
const canvas = document.createElement('canvas');
canvas.width = imageElement.width;
canvas.height = imageElement.height;
const ctx = canvas.getContext('2d');
ctx.drawImage(imageElement, 0, 0);
// 获取ImageData
const imageData = ctx.getImageData(0, 0, canvas.width, canvas.height);
// 创建ImageBitmap(更高效)
createImageBitmap(imageElement).then(imageBitmap => {
// 存储回调
this.taskQueue.set(taskId, { resolve, reject });
// 发送任务到Worker
this.worker.postMessage({
taskId,
operation,
imageData: imageBitmap,
params
}, [imageBitmap]);
});
});
}
destroy() {
this.worker.terminate();
this.taskQueue.clear();
}
}
// 使用示例
const imageProcessor = new ImageProcessingWorker();
async function processUserImage(imageFile) {
const img = new Image();
img.src = URL.createObjectURL(imageFile);
await new Promise(resolve => {
img.onload = resolve;
});
// 调整大小
const resized = await imageProcessor.processImage(img, 'resize', {
width: 800,
height: 600,
quality: 0.8
});
// 应用滤镜
const filtered = await imageProcessor.processImage(img, 'filter', {
filter: 'grayscale',
intensity: 1
});
// 压缩
const compressed = await imageProcessor.processImage(img, 'compress', {
quality: 0.6
});
return {
resized,
filtered,
compressed
};
}
十一、最佳实践与性能原则
11.1 性能优化黄金法则
- 测量第一,优化第二
- 使用Performance API测量关键指标
- 优先优化瓶颈,而非微观优化
- 建立性能基准线
- 延迟加载一切可能的内容
- 图片、视频、第三方脚本
- 非关键CSS和JavaScript
- 路由级代码分割
- 缓存一切可能的内容
- HTTP缓存策略
- 内存缓存频繁使用的数据
- 持久化缓存重要数据
- 批量处理操作
- DOM操作批量更新
- 网络请求合并
- 状态更新合并
- 避免阻塞主线程
- 长时间任务使用Web Worker
- 复杂计算使用时间切片
- 避免同步的阻塞操作
11.2 手写实现的优势
- 精细控制
- 可以根据具体需求定制优化策略
- 避免通用库的冗余代码
- 更好的理解
- 深入理解性能问题的本质
- 掌握底层优化原理
- 更小的包体积
- 只包含需要的功能
- 避免依赖大型库
- 更好的可调试性
- 完全控制代码流程
- 更容易添加日志和监控
11.3 持续优化流程
- 建立性能文化
- 性能作为核心需求
- 定期性能评审
- 性能回归测试
- 自动化性能测试
- 集成到CI/CD流程
- 自动生成性能报告
- 设置性能预算
- 渐进式优化
- 从最关键的问题开始
- 小步快跑,持续改进
- 监控优化效果
- 知识分享与传承
- 建立性能知识库
- 定期分享会
- 编写优化指南
总结
JavaScript性能优化是一个持续的过程,需要结合理论知识、实践经验和工具支持。通过手写实现这些优化技术,我们不仅能够解决具体的性能问题,更能深入理解性能优化的本质。
记住,最好的优化往往是那些能够从根本上解决问题的优化,而不是临时的修补。始终以用户体验为中心,以数据为依据,以持续改进为方法,才能构建出真正高性能的Web应用。
性能优化没有银弹,但有了这些手写实现的技术储备,你将能够更自信地面对各种性能挑战,构建出更快、更流畅的用户体验。