消除异步的传染性

消除异步的传染性

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 框架。

总结

  1. 生成器 + 执行器:用同步风格写异步代码,但需依赖执行器。
  2. 预加载 + 缓存:将异步操作提前完成,后续直接访问数据。
  3. 框架方案:如 React Suspense,在特定生态中隐藏异步细节。

JavaScript 无法完全消除异步传染性,但通过设计模式或工具可减少其对代码结构的影响。选择方案时需权衡代码复杂度、框架支持和实际需求。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容