消除异步的传染性
https://juejin.cn/post/7206297124940988472?searchId=202502111004349BF73B2AD669425A2367
使用抛出错误 执行多次的方式 抛出错误 中断错误 等待完成 缓存结果 然后再次执行 优先获取缓存结果 正常则 通过执行2次的形式 来最终实现消除异步的传染性
deepseek的答案
在 JavaScript 中,异步操作的“传染性”指的是当一个函数变为异步后,其调用链上的所有函数也必须处理异步性(如返回 Promise
或使用 async/await
)。要减少这种传染性,可以通过以下方法实现同步风格的异步代码:
方法 1:生成器函数 + 自动执行器
通过生成器(Generator)和 yield
暂停执行,配合自动执行器处理异步操作,使得代码看起来像同步风格。
示例代码
// 自动执行器
function runGenerator(generator) {
const iterator = generator();
function handle(iteration) {
if (iteration.done) return Promise.resolve(iteration.value);
return Promise.resolve(iteration.value)
.then((res) => handle(iterator.next(res)))
.catch((err) => handle(iterator.throw(err)));
}
return handle(iterator.next());
}
// 示例异步函数
function fetchData() {
return fetch("https://api.example.com/data").then((res) => res.json());
}
// 生成器函数(看似同步的代码)
function* main() {
try {
const data = yield fetchData(); // yield 暂停,等待异步操作完成
console.log("Data received:", data);
} catch (error) {
console.error("Error:", error);
}
}
// 执行
runGenerator(main);
关键点
- 生成器函数
main
使用yield
暂停,等待异步操作结果。 - 执行器
runGenerator
自动处理Promise
,将结果传回生成器。 - 调用链不强制要求
async/await
,但执行器返回Promise
,顶层仍需处理异步。
方法 2:Async/await 隔离
将异步操作封装在独立模块中,在程序初始化时预加载数据,后续直接使用同步数据。
示例代码
// 数据缓存模块
let cachedData = null;
async function initialize() {
cachedData = await fetch("https://api.example.com/data").then((res) => res.json());
}
// 初始化时预加载数据
initialize().catch(console.error);
// 后续使用同步数据(假设数据已加载)
function getData() {
if (!cachedData) throw new Error("Data not loaded");
return cachedData;
}
// 业务代码
function processData() {
const data = getData(); // 同步调用
console.log("Processing:", data);
}
关键点
- 异步操作集中在初始化阶段,后续代码直接访问缓存数据。
- 需确保数据加载完成后再调用同步方法,否则可能出错。
方法 3:React Suspense(框架特定方案)
在 React 中,通过 Suspense
组件处理异步渲染,使组件代码保持同步风格。
示例代码
// 封装异步资源的函数
function fetchResource(promise) {
let status = "pending";
let result;
const suspender = promise.then(
(res) => {
status = "success";
result = res;
},
(err) => {
status = "error";
result = err;
}
);
return {
read() {
if (status === "pending") throw suspender;
if (status === "error") throw result;
return result;
},
};
}
// 在组件中使用
const resource = fetchResource(fetch("https://api.example.com/data").then((r) => r.json()));
function MyComponent() {
const data = resource.read(); // 同步调用,但需要 Suspense 支持
return <div>{data}</div>;
}
// 在根组件包裹 Suspense
function App() {
return (
<Suspense fallback={<div>Loading...</div>}>
<MyComponent />
</Suspense>
);
}
关键点
-
resource.read()
在数据未就绪时抛出Promise
,由Suspense
捕获并显示加载状态。 - 组件代码无需处理异步逻辑,但需依赖 React 框架。
总结
- 生成器 + 执行器:用同步风格写异步代码,但需依赖执行器。
- 预加载 + 缓存:将异步操作提前完成,后续直接访问数据。
- 框架方案:如 React Suspense,在特定生态中隐藏异步细节。
JavaScript 无法完全消除异步传染性,但通过设计模式或工具可减少其对代码结构的影响。选择方案时需权衡代码复杂度、框架支持和实际需求。