消除異步的傳染性
https://juejin.cn/post/7206297124940988472?searchId=202502111004349BF73B2AD669425A2367
使用拋出錯(cuò)誤 執(zhí)行多次的方式 拋出錯(cuò)誤 中斷錯(cuò)誤 等待完成 緩存結(jié)果 然后再次執(zhí)行 優(yōu)先獲取緩存結(jié)果 正常則 通過執(zhí)行2次的形式 來最終實(shí)現(xiàn)消除異步的傳染性
deepseek的答案
在 JavaScript 中,異步操作的“傳染性”指的是當(dāng)一個(gè)函數(shù)變?yōu)楫惒胶?,其調(diào)用鏈上的所有函數(shù)也必須處理異步性(如返回 Promise 或使用 async/await)。要減少這種傳染性,可以通過以下方法實(shí)現(xiàn)同步風(fēng)格的異步代碼:
方法 1:生成器函數(shù) + 自動(dòng)執(zhí)行器
通過生成器(Generator)和 yield 暫停執(zhí)行,配合自動(dòng)執(zhí)行器處理異步操作,使得代碼看起來像同步風(fēng)格。
示例代碼
// 自動(dòng)執(zhí)行器
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());
}
// 示例異步函數(shù)
function fetchData() {
return fetch("https://api.example.com/data").then((res) => res.json());
}
// 生成器函數(shù)(看似同步的代碼)
function* main() {
try {
const data = yield fetchData(); // yield 暫停,等待異步操作完成
console.log("Data received:", data);
} catch (error) {
console.error("Error:", error);
}
}
// 執(zhí)行
runGenerator(main);
關(guān)鍵點(diǎn)
- 生成器函數(shù)
main使用yield暫停,等待異步操作結(jié)果。 - 執(zhí)行器
runGenerator自動(dòng)處理Promise,將結(jié)果傳回生成器。 - 調(diào)用鏈不強(qiáng)制要求
async/await,但執(zhí)行器返回Promise,頂層仍需處理異步。
方法 2:Async/await 隔離
將異步操作封裝在獨(dú)立模塊中,在程序初始化時(shí)預(yù)加載數(shù)據(jù),后續(xù)直接使用同步數(shù)據(jù)。
示例代碼
// 數(shù)據(jù)緩存模塊
let cachedData = null;
async function initialize() {
cachedData = await fetch("https://api.example.com/data").then((res) => res.json());
}
// 初始化時(shí)預(yù)加載數(shù)據(jù)
initialize().catch(console.error);
// 后續(xù)使用同步數(shù)據(jù)(假設(shè)數(shù)據(jù)已加載)
function getData() {
if (!cachedData) throw new Error("Data not loaded");
return cachedData;
}
// 業(yè)務(wù)代碼
function processData() {
const data = getData(); // 同步調(diào)用
console.log("Processing:", data);
}
關(guān)鍵點(diǎn)
- 異步操作集中在初始化階段,后續(xù)代碼直接訪問緩存數(shù)據(jù)。
- 需確保數(shù)據(jù)加載完成后再調(diào)用同步方法,否則可能出錯(cuò)。
方法 3:React Suspense(框架特定方案)
在 React 中,通過 Suspense 組件處理異步渲染,使組件代碼保持同步風(fēng)格。
示例代碼
// 封裝異步資源的函數(shù)
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(); // 同步調(diào)用,但需要 Suspense 支持
return <div>{data}</div>;
}
// 在根組件包裹 Suspense
function App() {
return (
<Suspense fallback={<div>Loading...</div>}>
<MyComponent />
</Suspense>
);
}
關(guān)鍵點(diǎn)
-
resource.read()在數(shù)據(jù)未就緒時(shí)拋出Promise,由Suspense捕獲并顯示加載狀態(tài)。 - 組件代碼無需處理異步邏輯,但需依賴 React 框架。
總結(jié)
- 生成器 + 執(zhí)行器:用同步風(fēng)格寫異步代碼,但需依賴執(zhí)行器。
- 預(yù)加載 + 緩存:將異步操作提前完成,后續(xù)直接訪問數(shù)據(jù)。
- 框架方案:如 React Suspense,在特定生態(tài)中隱藏異步細(xì)節(jié)。
JavaScript 無法完全消除異步傳染性,但通過設(shè)計(jì)模式或工具可減少其對(duì)代碼結(jié)構(gòu)的影響。選擇方案時(shí)需權(quán)衡代碼復(fù)雜度、框架支持和實(shí)際需求。