js三座大山之异步四-Promise的同步调用消除异步的传染性

js三座大山

一:函数式编程
js三座大山之函数1
js三座大山之函数2-作用域与动态this

二:面向对象编程
js三座大山之对象,继承,类,原型链

三:异步编程:
js三座大山之异步一单线程,event loope,宏任务&微任务
js三座大山之异步二异步方案
js三座大山之异步三promise本质
js三座大山之异步四-Promise的同步调用消除异步的传染性

异步的传染性

正常情况我们使用异步的async/await + promise运行代码时是这样的。

javascript 复制代码
const test3 = async () => {
  const res = await new Promise((resolve) => {
    setTimeout(() => {
      resolve(3);
    }, 2000);
  });
  console.log("test 3 res", res);
  return res;
};

const test2 = async () => {
  return test3();
};

const test1 = async () => {
  return test2();
};

const main = async () => {
  const res = await test1();
  console.log("test 最终结果 res", res);
};
main();

如上面的代码,我们在执行 test3 函数时返回了一个 promise 的执行结果,为了拿到promise异步回到的结果,我们必须加上 async 和 await 去等待结果的反馈。 这就导致了如果要使用 async 和 await 语法糖必须在函数使用的链路上全部都要使用 async 和 await,这就是所谓的异步传染性。看起来就很丑,有木有!

消除异步的传染性

  • 思考下能否同步的运行promsie获取结果,消除掉async/await呢?
  • 答案: 因为 js 是单线程,如果想要获取异步代码并且执行效果还得是同步的话可以利用报错 + 缓存 + event-loop
  1. 在test3中调用异步api,将当前promise实例作为异常内容抛出,同时外层捕获这个错误,因为是一个promise所以在上面注册callback函数,等待后续执行,然后退出函数。第一次调用执行结束。
  2. 异步api执行完成后加入task queue
  3. 执行栈从队列中取出任务,运行任务,调用之前注册的callback函数,callback里面先缓存结果然后再次运行main方法.
  4. 再次走到test3时,直接从缓存里面同步取结果返回即可。

封装KillAwaitPromise对象

实现了KillAwaitPromise对象支持多个异步promise的调用 同时消除了异步的传染性,使用该对象有几个条件需要注意:

  1. 内部的函数必须是纯函数,不能有副作用。`因为每遇到一个异步代码就需要暂停然后等待结果再次从头执行,所以函数都运行多次。
  2. 需要使用KillAwaitPromise.promise来生成promise对象
  3. 在外层通过KillAwaitPromise.execute来执行方法。

测试代码:

ini 复制代码
import KillAwaitPromise from "../src/kill-await-promise.js";

const test3 = () => {
  const res = KillAwaitPromise.promise((resolve) => {
    setTimeout(() => {
      resolve(3);
    }, 2000);
  });
  console.log("test 3 res", res);
  return res;
};

const test2 = () => {
  const res = KillAwaitPromise.promise((resolve) => {
    setTimeout(() => {
      resolve(2);
    }, 2000);
  });

  console.log("test 2 res", res);
  return test3();
};

const test1 = () => {
  const res1 = KillAwaitPromise.promise((resolve) => {
    setTimeout(() => {
      resolve(1);
    }, 2000);
  });

  console.log("test 1 res1", res1);
  const res2 = KillAwaitPromise.promise((resolve) => {
    setTimeout(() => {
      resolve(1.2);
    }, 2000);
  });

  console.log("test 1 res2", res2);
  return test2();
};

const main2 = () => {
  const res = test1();
  console.log("test 最终结果 res", res);
};

KillAwaitPromise.execute(main2);

输出结果:

bash 复制代码
test 1 res1 1
test 1 res1 1
test 1 res2 1.2
test 1 res1 1
test 1 res2 1.2
test 2 res 2
test 1 res1 1
test 1 res2 1.2
test 2 res 2
test 3 res 3
test 最终结果 res 3

KillAwaitPromise

ini 复制代码
let curCount = 0;
let map = new Map();
const promise = (callback) => {
  if (map.get(curCount)) {
    const res = map.get(curCount);
    curCount++;
    return res;
  }
  const p = new Promise(callback);
  throw p;
};

const run = (action) => {
  try {
    action();
  } catch (error) {
    error
      .then((res) => {
        map.set(curCount, res);
      })
      .catch((err) => {
        map.set(curCount, err);
      })
      .finally(() => {
        curCount = 0;
        run(action);
      });
  }
};

const execute = (task) => {
  curCount = 0;
  map = new Map();
  run(task);
};

export const KillAwaitPromise = {
  execute,
  promise,
};

export default KillAwaitPromise;
相关推荐
哆啦A梦15881 小时前
商城后台管理系统 03 登录布局
javascript·vue.js·elementui
曼巴UE51 小时前
UE FString, FName ,FText 三者转换,再次学习,官方文档理解
服务器·前端·javascript
selt7912 小时前
Redisson之RedissonLock源码完全解析
android·java·javascript
行走的陀螺仪2 小时前
高级前端 Input 公共组件设计方案(Vue3 + TypeScript)
前端·javascript·typescript·vue·组件设计方案
一颗不甘坠落的流星2 小时前
【Antd】基于 Upload 组件,导入Json文件并转换为Json数据
前端·javascript·json
LYFlied2 小时前
Vue2 与 Vue3 虚拟DOM更新原理深度解析
前端·javascript·vue.js·虚拟dom
小飞侠在吗3 小时前
vue shallowRef 与 shallowReacitive
前端·javascript·vue.js
GISer_Jing4 小时前
WebGL实例化渲染:性能提升策略
前端·javascript·webgl
Gomiko4 小时前
JavaScript进阶(四):DOM监听
开发语言·javascript·ecmascript
牛客企业服务4 小时前
2026年AI面试布局:破解规模化招聘的效率困局
人工智能·面试·职场和发展