经验首页 前端设计 程序设计 Java相关 移动开发 数据库/运维 软件/图像 大数据/云计算 其他经验
当前位置:技术经验 » JS/JS库/框架 » JavaScript » 查看文章
JavaScript手写异步加法asyncAdd方法详解
来源:jb51  时间:2022/8/23 15:36:57  对本文有异议

前言

在掘金上发现一道既简单但个人觉得还挺有意思的一道题,题目如下:

  1. // 异步加法
  2. function asyncAdd(a,b,cb){
  3. setTimeout(() => {
  4. cb(null, a + b)
  5. }, Math.random() * 1000)
  6. }
  7. async function total(){
  8. const res1 = await sum(1,2,3,4,5,6,4)
  9. const res2 = await sum(1,2,3,4,5,6,4)
  10. return [res1, res2]
  11. }
  12. total()
  13. // 实现下 sum 函数。注意不能使用加法,在 sum 中借助 asyncAdd 完成加法。尽可能的优化这个方法的时间。
  14. function sum(){
  15. }

你可以直接尝试实现下,考察下自己的思维和 JavaScript 基础知识的联系如何,大佬请绕行!

估计大多数人第一眼看下都不知道这题目到底要干啥(我不说就没人知道我也是),但是在看第二遍的时候估计就差不多明白具体是要考察什么内容了,下面就一起来分析分析吧!!!

分析 asyncAdd

这里先放置最终结论:

  • 只能修改 sum 部分的内容,sum 可接收任意长度的参数
  • sum 中只能通过 asyncAdd 实现加法计算
  • sum 中需要处理异步逻辑,需要使用 Promise
  • 需要优化 sum 方法的计算时间

下面是分别通过对代码的不同部分进行分析,获取到的相关的信息。

直观的基本要求

  1. // 实现下 sum 函数。注意不能使用加法,在 sum 中借助 asyncAdd 完成加法。尽可能的优化这个方法的时间。
  2. function sum(){ }

最直观的方式就是通过上述的文字描述部分,可以很容易知道题目具体要求:

  • 实现 sum 函数,即只能修改 sum 部分的内容
  • 不能直接使用加法(+),通过 asyncAdd 实现加法
  • 优化 sum 方法的计算时间

隐藏的考察点 — setTimeout & cb

  1. // 异步加法
  2. function asyncAdd(a, b, cb){
  3. setTimeout(() => {
  4. cb(null, a + b)
  5. }, Math.random() * 1000)
  6. }

从上述内容来看,最明显的就是 setTimeoutcb 了,其实这不难理解因为在 asyncAdd 中使用了 setTimeout 只能通过回调函数 cb 将本次计算结果返回出去,那其中的第一个参数 null 代表什么呢?

其实可以认为它是一个错误信息对象,如果你比较了解 node 的话,就会知道在 node 中的异步处理的回调函数通常第一个参数就是错误对象,用于传递给外部在发生错误时自定义后续执行逻辑等。

一句话: cb 函数会接收 错误对象 和 计算结果 作为参数传递给外部。

隐藏的考察点 — async & await

  1. async function total(){
  2. const res1 = await sum(1,2,3,4,5,6,4)
  3. const res2 = await sum(1,2,3,4,5,6,4)
  4. return [res1, res2]
  5. }

从上述的这部分来看,sum 方法的 返回值 肯定是一个 promise 类型的,因为最前面明显的使用了 await sum(...) 的形式。

另外 total 函数返回值也必然是一个 promise 类型,因为整个 total 函数被定义为了一个 async 异步函数,可点击此处查看详细内容

一句话:sum 需要返回 promise 类型的值,即 sum 一定会使用到 promise,并且从 sum(1,2,3,4,5,6,4) 可知 sum 可接收任意长度的参数。

实现 asyncAdd

具体实现

实现思路如下:

  • 考虑到外部参数长度不固定,使用剩余运算符接收所有传入的参数
  • 考虑到 asyncAdd 中的异步操作,将其封装为 Promise 的实现,即 caculate 函数
  • 考虑到 asyncAdd 实际只能一次接收两个数字进行计算,使用循环的形式将多个参数分别传入
  • 考虑到通过循环处理异步操作的顺序问题,使用 async/await 来保证正确的执行顺序,且 async 函数的返回值正好符合 sumPromise 类型的要求

具体代码如下:

  1. // 通过 ES6 的剩余运算符(...) 接收外部传入长度不固定的参数
  2. async function sum(...nums: number[]) {
  3. // 封装 Promise
  4. function caculate(num1: number, num2: number) {
  5. return new Promise((resolve, reject) => {
  6. // 调用 asyncAdd 实现加法
  7. asyncAdd(num1, num2, (err: any, rs: number) => {
  8. // 处理错误逻辑
  9. if (err) {
  10. reject(err);
  11. return;
  12. }
  13. // 向外部传递对应的计算结果
  14. resolve(rs);
  15. });
  16. })
  17. }
  18. let res: any = 0;
  19. // 通过遍历将参数一个个进行计算
  20. for (const n of nums) {
  21. // 为了避免异步执行顺序问题,使用 await 等待执行结果
  22. res = await caculate(res, n);
  23. }
  24. return res;
  25. }

进行优化

抽离内层函数

  • caculate 函数可抽离到 sum 函数外层
  • asyncAdd 函数的回调函数没必要抽离,因为它依赖的参数和外部方法太多
  1. function caculate(num1: number, num2: number) {
  2. return new Promise((resolve, reject) => {
  3. asyncAdd(num1, num2, (err: any, rs: number) => {
  4. if (err) {
  5. reject(err);
  6. return;
  7. }
  8. resolve(rs);
  9. });
  10. })
  11. }
  12. async function sum(...nums: number[]) {
  13. let res: any = 0;
  14. for (const n of nums) {
  15. res = await caculate(res, n);
  16. }
  17. return res;
  18. }

缓存计算结果

其实你仔细观察 total 方法,其中 sum 调用了两次,而且参数还是一模一样的,目的就是提示你在第二次计算相同内容时结果直接 从缓存中获取,而不是在通过异步计算。

  1. async function total(){
  2. const res1 = await sum(1,2,3,4,5,6,4)
  3. const res2 = await sum(1,2,3,4,5,6,4)
  4. return [res1, res2]
  5. }

以下只是一个简单的缓存方案的实现,不必过于纠结,具体实现如下:

  1. const cash: any = {};
  2. function isUndefined(target: any) {
  3. return target === void 0;
  4. }
  5. async function sum(...nums: number[]) {
  6. let res: any = 0;
  7. const key = nums.join('+');
  8. if (!isUndefined(cash[key])) return cash[key];
  9. for (const n of nums) {
  10. res = await caculate(res, n);
  11. }
  12. cash[key] = res;
  13. return res;
  14. }
  15. function caculate(num1: number, num2: number) {
  16. return new Promise((resolve, reject) => {
  17. asyncAdd(num1, num2, (err: any, rs: number) => {
  18. if (err) {
  19. reject(err);
  20. return;
  21. }
  22. resolve(rs);
  23. });
  24. })
  25. }

以上就是JavaScript手写异步加法asyncAdd方法详解的详细内容,更多关于JavaScript异步加法asyncAdd的资料请关注w3xue其它相关文章!

 友情链接:直通硅谷  点职佳  北美留学生论坛

本站QQ群:前端 618073944 | Java 606181507 | Python 626812652 | C/C++ 612253063 | 微信 634508462 | 苹果 692586424 | C#/.net 182808419 | PHP 305140648 | 运维 608723728

W3xue 的所有内容仅供测试,对任何法律问题及风险不承担任何责任。通过使用本站内容随之而来的风险与本站无关。
关于我们  |  意见建议  |  捐助我们  |  报错有奖  |  广告合作、友情链接(目前9元/月)请联系QQ:27243702 沸活量
皖ICP备17017327号-2 皖公网安备34020702000426号