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;
相关推荐
熊的猫41 分钟前
JS 中的类型 & 类型判断 & 类型转换
前端·javascript·vue.js·chrome·react.js·前端框架·node.js
别拿曾经看以后~2 小时前
【el-form】记一例好用的el-input输入框回车调接口和el-button按钮防重点击
javascript·vue.js·elementui
川石课堂软件测试2 小时前
性能测试|docker容器下搭建JMeter+Grafana+Influxdb监控可视化平台
运维·javascript·深度学习·jmeter·docker·容器·grafana
JerryXZR3 小时前
前端开发中ES6的技术细节二
前端·javascript·es6
problc3 小时前
Flutter中文字体设置指南:打造个性化的应用体验
android·javascript·flutter
Gavin_9153 小时前
【JavaScript】模块化开发
前端·javascript·vue.js
阑梦清川3 小时前
在鱼皮的模拟面试里面学习有感
学习·面试·职场和发展
懒大王爱吃狼4 小时前
Python教程:python枚举类定义和使用
开发语言·前端·javascript·python·python基础·python编程·python书籍
待磨的钝刨5 小时前
【格式化查看JSON文件】coco的json文件内容都在一行如何按照json格式查看
开发语言·javascript·json
前端青山10 小时前
Node.js-增强 API 安全性和性能优化
开发语言·前端·javascript·性能优化·前端框架·node.js