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;
相关推荐
qiyi.sky4 分钟前
JavaWeb——Vue组件库Element(3/6):常见组件:Dialog对话框、Form表单(介绍、使用、实际效果)
前端·javascript·vue.js
煸橙干儿~~7 分钟前
分析JS Crash(进程崩溃)
java·前端·javascript
哪 吒9 分钟前
华为OD机试 - 几何平均值最大子数(Python/JS/C/C++ 2024 E卷 200分)
javascript·python·华为od
安冬的码畜日常16 分钟前
【D3.js in Action 3 精译_027】3.4 让 D3 数据适应屏幕(下)—— D3 分段比例尺的用法
前端·javascript·信息可视化·数据可视化·d3.js·d3比例尺·分段比例尺
Q_w77421 小时前
一个真实可用的登录界面!
javascript·mysql·php·html5·网站登录
昨天;明天。今天。1 小时前
案例-任务清单
前端·javascript·css
一丝晨光1 小时前
C++、Ruby和JavaScript
java·开发语言·javascript·c++·python·c·ruby
夜流冰1 小时前
工具方法 - 面试中回答问题的技巧
面试·职场和发展
Front思1 小时前
vue使用高德地图
javascript·vue.js·ecmascript
惜.己2 小时前
javaScript基础(8个案例+代码+效果图)
开发语言·前端·javascript·vscode·css3·html5