
文章目录
-
- [2.3 发布-订阅模型(Pub-Sub)](#2.3 发布-订阅模型(Pub-Sub))
-
- [2.3.1 发布-订阅模型基础理论](#2.3.1 发布-订阅模型基础理论)
- [2.3.2 React中的发布-订阅实现](#2.3.2 React中的发布-订阅实现)
-
- [2.3.2.1 自定义事件总线实现](#2.3.2.1 自定义事件总线实现)
- [2.3.2.2 使用RxJS实现响应式Pub-Sub](#2.3.2.2 使用RxJS实现响应式Pub-Sub)
- [2.3.3 现代状态管理库中的Pub-Sub](#2.3.3 现代状态管理库中的Pub-Sub)
- [2.3.4 Pub-Sub模式的高级应用](#2.3.4 Pub-Sub模式的高级应用)
-
- [2.3.4.1 多主题复杂订阅](#2.3.4.1 多主题复杂订阅)
- [2.3.4.2 带历史记录的Event Bus](#2.3.4.2 带历史记录的Event Bus)
- [2.3.5 Pub-Sub模式的优缺点与最佳实践](#2.3.5 Pub-Sub模式的优缺点与最佳实践)
- [2.4 背压(Backpressure)](#2.4 背压(Backpressure))
-
- [2.4.1 背压概念解析](#2.4.1 背压概念解析)
- [2.4.2 React中的背压处理技术](#2.4.2 React中的背压处理技术)
-
- [2.4.2.1 防抖(Debounce)与节流(Throttle)](#2.4.2.1 防抖(Debounce)与节流(Throttle))
- [2.4.2.2 使用RxJS处理背压](#2.4.2.2 使用RxJS处理背压)
- [2.4.3 复杂数据流的背压管理](#2.4.3 复杂数据流的背压管理)
-
- [2.4.3.1 分页加载大数据集](#2.4.3.1 分页加载大数据集)
- [2.4.3.2 WebSocket高频数据处理](#2.4.3.2 WebSocket高频数据处理)
- [2.4.4 背压处理策略比较](#2.4.4 背压处理策略比较)
- [2.4.5 背压处理最佳实践](#2.4.5 背压处理最佳实践)
- [2.5 异步与非阻塞(Async & Non-blocking)](#2.5 异步与非阻塞(Async & Non-blocking))
-
- [2.5.1 异步编程基础](#2.5.1 异步编程基础)
- [2.5.2 React异步处理机制](#2.5.2 React异步处理机制)
-
- [2.5.2.1 使用useEffect处理副作用](#2.5.2.1 使用useEffect处理副作用)
- [2.5.2.2 使用useReducer管理复杂异步状态](#2.5.2.2 使用useReducer管理复杂异步状态)
- [2.5.3 高级异步模式](#2.5.3 高级异步模式)
-
- [2.5.3.1 竞态条件处理](#2.5.3.1 竞态条件处理)
- [2.5.3.2 并行与顺序请求](#2.5.3.2 并行与顺序请求)
- [2.5.4 非阻塞UI模式](#2.5.4 非阻塞UI模式)
-
- [2.5.4.1 过渡与Suspense](#2.5.4.1 过渡与Suspense)
- [2.5.4.2 使用useTransition优化用户体验](#2.5.4.2 使用useTransition优化用户体验)
- [2.5.5 异步最佳实践](#2.5.5 异步最佳实践)
- 总结

2.3 发布-订阅模型(Pub-Sub)

2.3.1 发布-订阅模型基础理论
发布-订阅模型(Publish-Subscribe Pattern,简称Pub-Sub)是一种消息传递范式,发送者(发布者)不直接将消息发送给特定接收者(订阅者),而是将发布的消息分为不同的类别,订阅者只接收感兴趣类别的消息。这种模式与观察者模式类似,但更加解耦,发布者完全不知道订阅者的存在。
Pub-Sub模型的核心组件:
- 发布者(Publisher):负责产生和发布消息到消息代理
- 订阅者(Subscriber):向消息代理注册感兴趣的主题,并接收相关消息
- 消息代理(Broker):作为中间人,负责接收发布者的消息并分发给所有相关订阅者
- 主题(Topic):消息的分类标识,订阅者根据主题选择接收哪些消息
2.3.2 React中的发布-订阅实现
在React生态系统中,发布-订阅模型有多种实现方式,从简单的自定义事件总线到复杂的状态管理库。
2.3.2.1 自定义事件总线实现
jsx
class EventBus {
constructor() {
this.events = {};
}
subscribe(event, callback) {
if (!this.events[event]) {
this.events[event] = [];
}
this.events[event].push(callback);
return () => {
this.events[event] = this.events[event].filter(cb => cb !== callback);
};
}
publish(event, ...args) {
if (!this.events[event]) return;
this.events[event].forEach(callback => callback(...args));
}
unsubscribe(event, callback) {
if (!this.events[event]) return;
this.events[event] = this.events[event].filter(cb => cb !== callback);
}
}
// 全局事件总线实例
const eventBus = new EventBus();
// 发布者组件
function Publisher() {
const handleClick = () => {
eventBus.publish('dataUpdate', { time: Date.now(), value: Math.random() });
};
return <button onClick={handleClick}>发布数据</button>;
}
// 订阅者组件
function Subscriber() {
const [message, setMessage] = useState('无数据');
useEffect(() => {
const unsubscribe = eventBus.subscribe('dataUpdate', (data) => {
setMessage(`收到数据: ${JSON.stringify(data)}`);
});
return () => unsubscribe();
}, []);
return <div>{message}</div>;
}
function App() {
return (
<div>
<Publisher />
<Subscriber />
</div>
);
}
2.3.2.2 使用RxJS实现响应式Pub-Sub
RxJS是一个强大的响应式编程库,非常适合实现复杂的Pub-Sub场景:
jsx
import { Subject } from 'rxjs';
// 创建主题
const dataSubject = new Subject();
// 发布者组件
function RxPublisher() {
const emitData = () => {
dataSubject.next({
id: Math.floor(Math.random() * 1000),
timestamp: new Date().toISOString()
});
};
return <button onClick={emitData}>发射数据</button>;
}
// 订阅者组件
function RxSubscriber() {
const [items, setItems] = useState([]);
useEffect(() => {
const subscription = dataSubject.subscribe(data => {
setItems(prev => [...prev, data]);
});
return () => subscription.unsubscribe();
}, []);
return (
<ul>
{items.map((item, index) => (
<li key={index}>
ID: {item.id} - Time: {item.timestamp}
</li>
))}
</ul>
);
}
function RxApp() {
return (
<div>
<RxPublisher />
<RxSubscriber />
</div>
);
}
2.3.3 现代状态管理库中的Pub-Sub
Redux等状态管理库的核心也基于Pub-Sub模式:
jsx
import { createStore } from 'redux';
// Reducer处理action并返回新状态
function counterReducer(state = { value: 0 }, action) {
switch (action.type) {
case 'INCREMENT':
return { value: state.value + 1 };
case 'DECREMENT':
return { value: state.value - 1 };
default:
return state;
}
}
// 创建store
const store = createStore(counterReducer);
// 发布action的函数
function dispatchAction(type) {
return () => store.dispatch({ type });
}
// 订阅者组件
function ReduxSubscriber() {
const [count, setCount] = useState(store.getState().value);
useEffect(() => {
const unsubscribe = store.subscribe(() => {
setCount(store.getState().value);
});
return () => unsubscribe();
}, []);
return <div>当前计数: {count}</div>;
}
function ReduxApp() {
return (
<div>
<button onClick={dispatchAction('INCREMENT')}>增加</button>
<button onClick={dispatchAction('DECREMENT')}>减少</button>
<ReduxSubscriber />
</div>
);
}
2.3.4 Pub-Sub模式的高级应用

2.3.4.1 多主题复杂订阅
jsx
const multiEventBus = {
topics: {},
subscribe(topic, callback) {
if (!this.topics[topic]) this.topics[topic] = [];
this.topics[topic].push(callback);
return () => {
this.topics[topic] = this.topics[topic].filter(cb => cb !== callback);
};
},
publish(topic, data) {
if (!this.topics[topic]) return;
this.topics[topic].forEach(cb => cb(data));
},
publishAll(data) {
Object.values(this.topics).flat().forEach(cb => cb(data));
}
};
function MultiPublisher() {
const publishUser = () => multiEventBus.publish('user', { name: 'Alice', age: 25 });
const publishProduct = () => multiEventBus.publish('product', { id: 1, name: 'Laptop' });
const publishToAll = () => multiEventBus.publishAll({ type: 'SYSTEM', message: 'Refresh all' });
return (
<div>
<button onClick={publishUser}>发布用户数据</button>
<button onClick={publishProduct}>发布产品数据</button>
<button onClick={publishToAll}>全局通知</button>
</div>
);
}
function UserSubscriber() {
const [user, setUser] = useState(null);
useEffect(() => {
return multiEventBus.subscribe('user', setUser);
}, []);
return <div>用户: {user ? JSON.stringify(user) : '无'}</div>;
}
function ProductSubscriber() {
const [product, setProduct] = useState(null);
useEffect(() => {
return multiEventBus.subscribe('product', setProduct);
}, []);
return <div>产品: {product ? JSON.stringify(product) : '无'}</div>;
}
function GlobalSubscriber() {
const [message, setMessage] = useState('等待全局消息...');
useEffect(() => {
const unsubUser = multiEventBus.subscribe('user', () => setMessage('收到用户更新'));
const unsubProduct = multiEventBus.subscribe('product', () => setMessage('收到产品更新'));
const unsubAll = multiEventBus.subscribe('*', (data) => setMessage(`全局: ${data.message}`));
return () => {
unsubUser();
unsubProduct();
unsubAll();
};
}, []);
return <div>{message}</div>;
}
function MultiPubSubApp() {
return (
<div>
<MultiPublisher />
<UserSubscriber />
<ProductSubscriber />
<GlobalSubscriber />
</div>
);
}
2.3.4.2 带历史记录的Event Bus
jsx
class HistoryEventBus {
constructor() {
this.events = {};
this.history = new Map();
}
subscribe(event, callback) {
if (!this.events[event]) this.events[event] = [];
this.events[event].push(callback);
// 如果有历史记录,立即通知
if (this.history.has(event)) {
callback(this.history.get(event));
}
return () => {
this.events[event] = this.events[event].filter(cb => cb !== callback);
};
}
publish(event, data, saveToHistory = false) {
if (saveToHistory) {
this.history.set(event, data);
}
if (!this.events[event]) return;
this.events[event].forEach(callback => callback(data));
}
getHistory(event) {
return this.history.get(event);
}
}
const historyBus = new HistoryEventBus();
function HistoryPublisher() {
const [value, setValue] = useState('');
const publishWithHistory = () => {
historyBus.publish('withHistory', value, true);
};
const publishWithoutHistory = () => {
historyBus.publish('noHistory', value, false);
};
return (
<div>
<input value={value} onChange={e => setValue(e.target.value)} />
<button onClick={publishWithHistory}>发布(保存历史)</button>
<button onClick={publishWithoutHistory}>发布(不保存)</button>
</div>
);
}
function HistorySubscriber() {
const [withHistory, setWithHistory] = useState('');
const [noHistory, setNoHistory] = useState('');
useEffect(() => {
const unsub1 = historyBus.subscribe('withHistory', setWithHistory);
const unsub2 = historyBus.subscribe('noHistory', setNoHistory);
// 获取历史记录
const historyValue = historyBus.getHistory('withHistory');
if (historyValue) {
setWithHistory(`历史记录: ${historyValue}`);
}
return () => {
unsub1();
unsub2();
};
}, []);
return (
<div>
<div>带历史: {withHistory || '无'}</div>
<div>不带历史: {noHistory || '无'}</div>
</div>
);
}
function HistoryApp() {
return (
<div>
<HistoryPublisher />
<HistorySubscriber />
</div>
);
}
2.3.5 Pub-Sub模式的优缺点与最佳实践
优点:
- 松耦合:发布者和订阅者完全解耦,互不知晓对方存在
- 可扩展性:可以轻松添加新的发布者或订阅者而不影响现有系统
- 灵活性:支持一对多、多对多通信模式
- 动态性:订阅关系可以在运行时动态建立和解除
缺点:
- 调试困难:消息流可能难以追踪,特别是复杂的发布订阅关系
- 性能问题:大量消息可能导致系统性能下降
- 消息顺序:不能保证消息的接收顺序与发送顺序一致
- 内存泄漏:忘记取消订阅可能导致内存泄漏
最佳实践:
- 合理设计主题结构:避免主题过于宽泛或过于具体
- 使用强类型:为消息定义明确的类型和结构
- 及时取消订阅:在组件卸载时务必取消订阅
- 限制消息量:避免高频发布大量小消息
- 考虑错误处理:设计良好的错误处理机制
- 文档化消息协议:清晰记录所有主题和消息格式
2.4 背压(Backpressure)
2.4.1 背压概念解析
背压(Backpressure)是流处理系统中的一种重要概念,指当下游处理速度跟不上上游数据产生速度时,系统需要采取的策略和机制来应对这种不平衡。在React和前端开发中,背压处理尤为重要,因为浏览器环境对资源使用有严格限制。
背压问题常见场景:
- WebSocket高频数据推送
- 大规模实时数据可视化
- 文件上传/下载处理
- 高频率的用户事件(如滚动、鼠标移动)
- 与后端的高频轮询通信
2.4.2 React中的背压处理技术
2.4.2.1 防抖(Debounce)与节流(Throttle)
jsx
import { useState, useEffect } from 'react';
import { debounce, throttle } from 'lodash';
function ScrollMonitor() {
const [scrollPosition, setScrollPosition] = useState(0);
const [debouncedPos, setDebouncedPos] = useState(0);
const [throttledPos, setThrottledPos] = useState(0);
useEffect(() => {
const handleScroll = () => {
const position = window.pageYOffset;
setScrollPosition(position);
};
const handleDebouncedScroll = debounce(() => {
setDebouncedPos(window.pageYOffset);
}, 200);
const handleThrottledScroll = throttle(() => {
setThrottledPos(window.pageYOffset);
}, 200);
window.addEventListener('scroll', handleScroll);
window.addEventListener('scroll', handleDebouncedScroll);
window.addEventListener('scroll', handleThrottledScroll);
return () => {
window.removeEventListener('scroll', handleScroll);
window.removeEventListener('scroll', handleDebouncedScroll);
window.removeEventListener('scroll', handleThrottledScroll);
handleDebouncedScroll.cancel();
handleThrottledScroll.cancel();
};
}, []);
return (
<div style={{ height: '2000px' }}>
<div style={{ position: 'fixed', top: 0, left: 0, background: 'white' }}>
<div>原始位置: {scrollPosition}</div>
<div>防抖位置: {debouncedPos}</div>
<div>节流位置: {throttledPos}</div>
</div>
</div>
);
}
2.4.2.2 使用RxJS处理背压
RxJS提供了多种背压策略操作符:
jsx
import { fromEvent, Subject } from 'rxjs';
import { throttleTime, auditTime, sampleTime, bufferCount } from 'rxjs/operators';
function RxBackpressure() {
const [events, setEvents] = useState([]);
const [subject] = useState(new Subject());
useEffect(() => {
const subscription = subject
.pipe(
// 选择一种背压策略
// throttleTime(200), // 节流 - 每200ms最多一个值
// auditTime(200), // 审计 - 在200ms窗口结束时发出最新值
// sampleTime(200), // 采样 - 每200ms取一个样本值
bufferCount(5) // 缓冲 - 每5个值作为数组发出一次
)
.subscribe(value => {
setEvents(prev => [...prev, value]);
});
const mouseMove$ = fromEvent(document, 'mousemove');
const mouseSub = mouseMove$.subscribe(e => {
subject.next({ x: e.clientX, y: e.clientY, time: Date.now() });
});
return () => {
subscription.unsubscribe();
mouseSub.unsubscribe();
};
}, [subject]);
return (
<div>
<h2>鼠标移动事件(带背压处理)</h2>
<div style={{ height: '500px', border: '1px solid #ccc' }}>
{events.map((event, i) => (
<div key={i}>
{Array.isArray(event)
? `批量: ${event.length}个事件`
: `位置: ${event.x}, ${event.y}`}
</div>
))}
</div>
</div>
);
}
2.4.3 复杂数据流的背压管理

2.4.3.1 分页加载大数据集
jsx
function LargeDataLoader() {
const [data, setData] = useState([]);
const [loading, setLoading] = useState(false);
const [page, setPage] = useState(1);
const [hasMore, setHasMore] = useState(true);
const loadMore = useCallback(async () => {
if (loading || !hasMore) return;
setLoading(true);
try {
// 模拟API调用
await new Promise(resolve => setTimeout(resolve, 500));
const newData = Array.from({ length: 20 }, (_, i) =>
`项目 ${(page - 1) * 20 + i + 1}`
);
setData(prev => [...prev, ...newData]);
setPage(prev => prev + 1);
setHasMore(page < 5); // 假设总共5页数据
} finally {
setLoading(false);
}
}, [page, loading, hasMore]);
const handleScroll = useCallback(() => {
const { scrollTop, clientHeight, scrollHeight } = document.documentElement;
if (scrollHeight - (scrollTop + clientHeight) < 100) {
loadMore();
}
}, [loadMore]);
useEffect(() => {
window.addEventListener('scroll', handleScroll);
return () => window.removeEventListener('scroll', handleScroll);
}, [handleScroll]);
useEffect(() => {
loadMore();
}, []);
return (
<div>
<h2>大数据集分页加载</h2>
<ul style={{ height: '80vh', overflowY: 'auto' }}>
{data.map((item, index) => (
<li key={index} style={{ padding: '20px', borderBottom: '1px solid #eee' }}>
{item}
</li>
))}
{loading && <li>加载中...</li>}
{!hasMore && <li>没有更多数据了</li>}
</ul>
</div>
);
}
2.4.3.2 WebSocket高频数据处理
jsx
import { useState, useEffect, useRef } from 'react';
function WebSocketBackpressure() {
const [messages, setMessages] = useState([]);
const [displayMessages, setDisplayMessages] = useState([]);
const [isConnected, setIsConnected] = useState(false);
const [throttleEnabled, setThrottleEnabled] = useState(true);
const wsRef = useRef(null);
const lastUpdateRef = useRef(0);
useEffect(() => {
// 模拟WebSocket连接
wsRef.current = {
send: (message) => console.log('发送:', message),
close: () => {
setIsConnected(false);
console.log('连接关闭');
}
};
// 模拟接收消息
const interval = setInterval(() => {
if (isConnected) {
const newMessage = {
id: Date.now(),
value: Math.random(),
timestamp: new Date().toISOString()
};
setMessages(prev => [...prev, newMessage]);
}
}, 50); // 每秒约20条消息
return () => clearInterval(interval);
}, [isConnected]);
useEffect(() => {
if (!throttleEnabled) {
setDisplayMessages(messages);
return;
}
const interval = setInterval(() => {
if (messages.length > displayMessages.length) {
const newMessages = messages.slice(displayMessages.length);
setDisplayMessages(prev => [...prev, ...newMessages.slice(0, 5)]); // 每次最多5条
}
}, 200); // 每秒更新5次,最多25条/秒
return () => clearInterval(interval);
}, [messages, displayMessages, throttleEnabled]);
const toggleConnection = () => {
setIsConnected(prev => !prev);
if (!isConnected) {
setMessages([]);
setDisplayMessages([]);
}
};
return (
<div>
<h2>WebSocket高频数据处理</h2>
<div>
<button onClick={toggleConnection}>
{isConnected ? '断开连接' : '建立连接'}
</button>
<label>
<input
type="checkbox"
checked={throttleEnabled}
onChange={() => setThrottleEnabled(!throttleEnabled)}
/>
启用背压处理
</label>
</div>
<div>
<p>接收消息数: {messages.length}</p>
<p>显示消息数: {displayMessages.length}</p>
</div>
<div style={{ height: '300px', overflowY: 'auto', border: '1px solid #ccc' }}>
{displayMessages.map(msg => (
<div key={msg.id} style={{ padding: '5px', borderBottom: '1px solid #eee' }}>
{msg.value.toFixed(4)} @ {msg.timestamp}
</div>
))}
</div>
</div>
);
}

2.4.4 背压处理策略比较
策略 | 描述 | 适用场景 | React实现示例 |
---|---|---|---|
防抖(Debounce) | 事件触发后等待一段时间再处理,若期间有新事件则重新计时 | 搜索框输入、窗口大小调整 | lodash.debounce |
节流(Throttle) | 固定时间间隔内最多处理一次事件 | 滚动事件、鼠标移动 | lodash.throttle |
采样(Sampling) | 定期取最新值进行处理 | 实时数据监控 | RxJS sampleTime |
缓冲(Buffering) | 收集多个事件后批量处理 | 日志记录、分析数据收集 | RxJS bufferCount |
丢弃(Dropping) | 当处理不过来时丢弃部分事件 | 极高频率事件处理 | 自定义实现 |
分页(Pagination) | 分批加载处理数据 | 大数据集展示 | 滚动加载实现 |
2.4.5 背压处理最佳实践
- 识别性能瓶颈:使用DevTools分析应用性能,确定是否需要背压处理
- 选择合适的策略:根据场景选择防抖、节流、采样等不同策略
- 合理设置时间参数:太短达不到效果,太长影响用户体验
- 考虑内存管理:对于缓冲策略,注意控制缓冲区大小
- 提供用户反馈:当主动丢弃数据时,应通知用户
- 测试极端情况:模拟高负载情况测试背压处理效果
- 结合Web Worker:对于CPU密集型任务,考虑使用Web Worker分担主线程压力
2.5 异步与非阻塞(Async & Non-blocking)
2.5.1 异步编程基础
异步编程是现代JavaScript和React开发的核心概念,它允许程序在等待耗时操作(如网络请求、文件I/O)完成时继续执行其他任务,而不是阻塞整个应用。
React中的常见异步场景:
- 数据获取(API调用)
- 定时操作(setTimeout/setInterval)
- 事件处理(用户交互、WebSocket)
- 动画和过渡效果
- 懒加载组件和代码分割
2.5.2 React异步处理机制
2.5.2.1 使用useEffect处理副作用
jsx
function AsyncDataLoader() {
const [data, setData] = useState(null);
const [loading, setLoading] = useState(false);
const [error, setError] = useState(null);
useEffect(() => {
const fetchData = async () => {
setLoading(true);
setError(null);
try {
// 模拟API调用
await new Promise(resolve => setTimeout(resolve, 1000));
const mockData = {
userId: 1,
id: 1,
title: '异步加载的React数据',
completed: false
};
setData(mockData);
} catch (err) {
setError(err.message || '请求失败');
} finally {
setLoading(false);
}
};
fetchData();
}, []);
if (loading) return <div>加载中...</div>;
if (error) return <div>错误: {error}</div>;
if (!data) return null;
return (
<div>
<h2>{data.title}</h2>
<p>用户ID: {data.userId}</p>
</div>
);
}
2.5.2.2 使用useReducer管理复杂异步状态
jsx
function asyncReducer(state, action) {
switch (action.type) {
case 'FETCH_START':
return { ...state, loading: true, error: null };
case 'FETCH_SUCCESS':
return { loading: false, error: null, data: action.payload };
case 'FETCH_ERROR':
return { ...state, loading: false, error: action.payload };
default:
throw new Error(`未知action类型: ${action.type}`);
}
}
function ReducerAsyncDemo() {
const [state, dispatch] = useReducer(asyncReducer, {
loading: false,
error: null,
data: null
});
const fetchData = useCallback(async () => {
dispatch({ type: 'FETCH_START' });
try {
// 模拟API调用
await new Promise((resolve, reject) => {
setTimeout(() => {
Math.random() > 0.3
? resolve({
id: 1,
name: 'Reducer管理的数据',
value: Math.random()
})
: reject(new Error('随机模拟错误'));
}, 800);
}).then(data => {
dispatch({ type: 'FETCH_SUCCESS', payload: data });
});
} catch (error) {
dispatch({ type: 'FETCH_ERROR', payload: error.message });
}
}, []);
return (
<div>
<button onClick={fetchData} disabled={state.loading}>
{state.loading ? '加载中...' : '获取数据'}
</button>
{state.error && <div style={{ color: 'red' }}>错误: {state.error}</div>}
{state.data && (
<div>
<h3>{state.data.name}</h3>
<p>值: {state.data.value}</p>
</div>
)}
</div>
);
}
2.5.3 高级异步模式

2.5.3.1 竞态条件处理
jsx
function RaceConditionDemo() {
const [query, setQuery] = useState('');
const [results, setResults] = useState([]);
const [loading, setLoading] = useState(false);
const requestRef = useRef(null);
useEffect(() => {
if (!query.trim()) {
setResults([]);
return;
}
const currentRequest = {};
requestRef.current = currentRequest;
const search = async () => {
setLoading(true);
try {
// 模拟API调用
await new Promise(resolve =>
setTimeout(resolve, 500 + Math.random() * 1000)
);
// 检查是否是最新的请求
if (requestRef.current !== currentRequest) return;
const mockResults = Array.from({ length: 5 }, (_, i) => ({
id: `${query}-${i}`,
title: `${query} 结果 ${i + 1}`,
relevance: Math.random()
})).sort((a, b) => b.relevance - a.relevance);
setResults(mockResults);
} finally {
if (requestRef.current === currentRequest) {
setLoading(false);
}
}
};
const timer = setTimeout(search, 300); // 防抖延迟
return () => {
clearTimeout(timer);
};
}, [query]);
return (
<div>
<input
type="text"
value={query}
onChange={e => setQuery(e.target.value)}
placeholder="搜索..."
/>
{loading && <div>搜索中...</div>}
<ul>
{results.map(result => (
<li key={result.id}>{result.title}</li>
))}
</ul>
</div>
);
}
2.5.3.2 并行与顺序请求
jsx
function MultiRequestDemo() {
const [userData, setUserData] = useState(null);
const [postsData, setPostsData] = useState([]);
const [loading, setLoading] = useState(false);
const [progress, setProgress] = useState(0);
const fetchSequentially = async () => {
setLoading(true);
setProgress(0);
try {
// 第一个请求 - 用户数据
const user = await fetchMockAPI('/user/1', 800);
setUserData(user);
setProgress(33);
// 第二个请求 - 用户帖子
const posts = await fetchMockAPI('/posts?userId=1', 600);
setPostsData(posts);
setProgress(66);
// 第三个请求 - 用户好友
const friends = await fetchMockAPI('/friends/1', 400);
setProgress(100);
console.log('所有数据:', { user, posts, friends });
} catch (error) {
console.error('请求失败:', error);
} finally {
setLoading(false);
}
};
const fetchInParallel = async () => {
setLoading(true);
setProgress(0);
try {
const [user, posts, friends] = await Promise.all([
fetchMockAPI('/user/1', 800),
fetchMockAPI('/posts?userId=1', 600),
fetchMockAPI('/friends/1', 400)
]);
setUserData(user);
setPostsData(posts);
setProgress(100);
console.log('所有数据:', { user, posts, friends });
} catch (error) {
console.error('请求失败:', error);
} finally {
setLoading(false);
}
};
// 模拟API调用
const fetchMockAPI = async (endpoint, delay) => {
await new Promise(resolve => setTimeout(resolve, delay));
return { endpoint, data: `模拟数据 ${delay}ms` };
};
return (
<div>
<h2>并行与顺序请求</h2>
<div>
<button onClick={fetchSequentially} disabled={loading}>
顺序请求
</button>
<button onClick={fetchInParallel} disabled={loading}>
并行请求
</button>
</div>
{loading && (
<div>
<progress value={progress} max="100" />
{progress}%
</div>
)}
<div>
<h3>用户数据</h3>
<pre>{JSON.stringify(userData, null, 2)}</pre>
<h3>帖子数据</h3>
<pre>{JSON.stringify(postsData, null, 2)}</pre>
</div>
</div>
);
}
2.5.4 非阻塞UI模式

2.5.4.1 过渡与Suspense
jsx
import { Suspense, useState, useEffect } from 'react';
// 模拟异步资源
function createResource(promise) {
let status = 'pending';
let result;
let suspender = promise.then(
r => {
status = 'success';
result = r;
},
e => {
status = 'error';
result = e;
}
);
return {
read() {
if (status === 'pending') throw suspender;
if (status === 'error') throw result;
return result;
}
};
}
function fetchUser(id) {
return new Promise(resolve => {
setTimeout(() => {
resolve({
id,
name: `用户 ${id}`,
email: `user${id}@example.com`
});
}, 2000);
});
}
function UserProfile({ resource }) {
const user = resource.read();
return (
<div>
<h2>{user.name}</h2>
<p>Email: {user.email}</p>
</div>
);
}
function SuspenseDemo() {
const [userId, setUserId] = useState(1);
const [resource, setResource] = useState(createResource(fetchUser(1)));
const handleChange = e => {
const newId = parseInt(e.target.value);
setUserId(newId);
setResource(createResource(fetchUser(newId)));
};
return (
<div>
<div>
<label>
选择用户ID:
<select value={userId} onChange={handleChange}>
{[1, 2, 3, 4, 5].map(id => (
<option key={id} value={id}>{id}</option>
))}
</select>
</label>
</div>
<Suspense fallback={<div>加载用户数据...</div>}>
<UserProfile resource={resource} />
</Suspense>
</div>
);
}
2.5.4.2 使用useTransition优化用户体验
jsx
function TransitionDemo() {
const [resource, setResource] = useState(createResource(fetchUser(1)));
const [isPending, startTransition] = useTransition();
const [userId, setUserId] = useState(1);
const handleChange = e => {
const newId = parseInt(e.target.value);
setUserId(newId);
// 使用startTransition标记为非紧急更新
startTransition(() => {
setResource(createResource(fetchUser(newId)));
});
};
return (
<div>
<div>
<label>
选择用户ID:
<select value={userId} onChange={handleChange}>
{[1, 2, 3, 4, 5].map(id => (
<option key={id} value={id}>{id}</option>
))}
</select>
</label>
{isPending && <span style={{ marginLeft: '10px' }}>加载中...</span>}
</div>
<Suspense fallback={<div>加载用户数据...</div>}>
<UserProfile resource={resource} />
</Suspense>
</div>
);
}

2.5.5 异步最佳实践
- 错误处理:始终处理Promise拒绝情况,避免未捕获的Promise
- 取消机制:为长时间运行的异步操作实现取消功能
- 加载状态:提供清晰的加载状态反馈
- 竞态条件防护:使用ref或取消token防止过时响应
- 资源清理:在组件卸载时清理异步操作
- 性能优化:合理使用并行请求和懒加载
- 用户体验:考虑使用骨架屏(Skeleton)等优化技术
- 测试策略:编写全面的测试覆盖各种异步场景
总结
React的异步与非阻塞编程模型是现代前端开发的核心。通过合理运用Promise、async/await、Suspense等特性,结合useEffect、useReducer等Hooks,开发者可以构建响应迅速、用户体验良好的应用程序。关键在于理解JavaScript的事件循环机制和React的渲染周期,从而避免常见的性能陷阱和竞态条件问题。随着React并发模式的不断发展,异步处理能力将变得更加强大和灵活。