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;
相关推荐
绝知此事2 小时前
【算法突围 01】线性结构与哈希表:后端开发的收纳术
java·数据结构·算法·面试·jdk·散列表
放下华子我只抽RuiKe52 小时前
React 从入门到生产(四):自定义 Hook
前端·javascript·人工智能·深度学习·react.js·自然语言处理·前端框架
XinZong3 小时前
OpenClaw 实现双重心跳(Heartbeat)+ clawreach虾聊项目实现
javascript
kyriewen4 小时前
面试官让我查各部门工资最高的员工,我用AI三秒写出窗口函数,他愣了
后端·mysql·面试
还有多久拿退休金5 小时前
一张栈的图,治好你面试答不出 script 阻塞的病
前端·javascript
zithern_juejin5 小时前
原型与原型链
javascript
罗超驿5 小时前
18.事务的隔离性和隔离级别:MySQL面试高频考点全解析
数据库·mysql·面试
做人求其滴6 小时前
面试经典 150 题 380 274
c++·算法·面试·职场和发展·力扣
小江的记录本6 小时前
【Java基础】Java 8-21新特性:JDK21 LTS:虚拟线程、模式匹配switch、结构化并发、序列集合(附《思维导图》+《面试高频考点清单》)
java·数据库·python·mysql·spring·面试·maven
ricardo19737 小时前
# Tree Shaking 深度解析:为什么你的代码没被摇掉?
前端·面试