Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

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

Open
wuyunqiang opened this issue Jan 2, 2024 · 0 comments
Labels

Comments

@wuyunqiang
Copy link
Owner

wuyunqiang commented Jan 2, 2024

js三座大山

一:函数式编程

js三座大山之函数1 

js三座大山之函数2-作用域与动态this

二:面向对象编程

js三座大山之对象,继承,类,原型链

三:异步编程:

js三座大山之异步一单线程,event loope,宏任务&微任务

js三座大山之异步二异步方案

js三座大山之异步三promise本质

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

js三座大山之异步五基于异步的js性能优化

异步的传染性

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

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

截屏2023-12-30 下午5.21.15.png

  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来执行方法。

测试代码:

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);

输出结果:

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

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;
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

1 participant