JavaScript手写异步加法asyncAdd方法详解

目录
  • 前言
  • 分析 asyncAdd
    • 直观的基本要求
    • 隐藏的考察点 — setTimeout & cb
    • 隐藏的考察点 — async & await
  • 实现 asyncAdd
    • 具体实现
    • 进行优化
      • 抽离内层函数
      • 缓存计算结果

前言

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

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

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

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

分析 asyncAdd

这里先放置最终结论:

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

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

直观的基本要求

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

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

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

隐藏的考察点 — setTimeout & cb

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

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

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

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

隐藏的考察点 — async & await

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

从上述的这部分来看,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 类型的要求

具体代码如下:

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

进行优化

抽离内层函数

  • caculate 函数可抽离到 sum 函数外层
  • asyncAdd 函数的回调函数没必要抽离,因为它依赖的参数和外部方法太多
function caculate(num1: number, num2: number) {
    return new Promise((resolve, reject) => {
        asyncAdd(num1, num2, (err: any, rs: number) => {
            if (err) {
                reject(err);
                return;
            }
            resolve(rs);
        });
    })
}
async function sum(...nums: number[]) {
    let res: any = 0;
    for (const n of nums) {
        res = await caculate(res, n);
    }
    return res;
}

缓存计算结果

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

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

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

const cash: any = {};
function isUndefined(target: any) {
    return target === void 0;
}
async function sum(...nums: number[]) {
    let res: any = 0;
    const key = nums.join('+');
    if (!isUndefined(cash[key])) return cash[key];
    for (const n of nums) {
        res = await caculate(res, n);
    }
    cash[key] = res;
    return res;
}
function caculate(num1: number, num2: number) {
    return new Promise((resolve, reject) => {
        asyncAdd(num1, num2, (err: any, rs: number) => {
            if (err) {
                reject(err);
                return;
            }
            resolve(rs);
        });
    })
}

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

(0)

相关推荐

  • JavaScript事件循环同步任务与异步任务

    目录 前言 执行栈与任务队列 执行栈 任务队列 同步任务与异步任务 同步任务 异步任务 js的执行机制 结语 前言 首先,在学习js中同步异步的问题前,需要明白,js是单线程的,为什么它得是单线程的呢?这得从它的使用场景来看了,它主要是用来让用户与页面进行交互的吧.那么假设js是多线程的,在这个线程里面,用户点击某个按钮会增加一个DOM节点,在另一个线程里面,用户点击这个按钮又会删除一个DOM节点,那么此时js就不知道该听谁的了.那同步异步的出现又是为了什么呢?假设没有异步,那么我们在向服务器请

  • JavaScript异步队列进行try catch时的问题解决

    目录 一.前言 二.主要讲的异步队列方法 2.1 setTimeout 2.1.1 问题表现 2.1.2 问题原因 2.2 Promise 2.3 callback 2.4 Async await 一.前言 我们在写js的时候,经常的会遇到需要异步去请求接口,或者通过setTimeout或Promise去做什么事, 然后让同步进程继续向下走, 当到某个时间节点的时候或者数据请求成功的时候在通过eventloop的方式回调执行.这本身是js的特点和优势. 但是,异步队列执行也存在错误的情况,这时,

  • Javascript处理循环的异步操作指南

    目录 案例: 一.async/await处理思路 二.递归处理思路 总结 案例: compute.exec()这是个异步方法,在里面处理一些实际业务,这时候打印出来的很可能就是300,300,300(因为异步for循环还没有等异步操作返回Promise对象过来i值已经改变成300了) function getMoney(){ var money=[100,200,300] for(let i=0; i<money.length; i++){ compute.exec().then(()=>{

  • JS异步观察目标元素方式完成分页加载

    目录 介绍 代码 演示 正文 监听元素 反复交叉 结语 介绍 平时我们处理分页加载时,往往是通过滚动条判断是否到了容器底部再执行的加载任务的,这样有一个问题就是,不管滚动条是否划到底部位置,它都会运行计算这个函数. 那么,如果能判断底部的加载区域出现后再去执行加载,不用再做滚动条计算了,这样岂不美哉.本期将以异步观察目标元素的新方式去完成分页加载业务. 代码 <div id="app" @vue:mounted="mounted" :class="{

  • javascript实现一个数值加法函数

    废话不多说,直接奉上代码 JS <script type="text/javascript"> function Sum(arg1,arg2){ //数值加法函数 var sarg1 = new String(arg1); //将传入的参数转为字符串以便进行参数检查 var sarg2 = new String(arg2); //将参数2转为字符类型 if( (sarg1=="")||(sarg2=="") ) //确保参数不为空 {

  • JS异步的执行顺序分析

    1.js的执行顺序,先同步后异步 2.异步中任务队列的执行顺序: 先微任务microtask队列,再宏任务macrotask队列 3.调用Promise 中的resolve,reject属于微任务队列,setTimeout属于宏任务队列 注意以上都是 队列,先进先出. 微任务包括 `process.nextTick` ,`promise` ,`MutationObserver`. 宏任务包括 `script` , `setTimeout` ,`setInterval` ,`setImmediat

  • JavaScript Generator异步过度的实现详解

    目录 异步过渡方案Generator 1. Generator 的使用 2. Generator 函数的执行 2.1 yield 关键字 2.2 next 方法与 Iterator 接口 3. Generator 中的错误处理 4. 用 Generator 组织异步方法 5. Generator 的自动执行 5.1 自动执行器的实现 5.2 基于Promise的执行器 5.3 使用 co 模块来自动执行 异步过渡方案Generator 在使用 Generator 前,首先知道 Generator

  • JavaScript手写异步加法asyncAdd方法详解

    目录 前言 分析 asyncAdd 直观的基本要求 隐藏的考察点 — setTimeout & cb 隐藏的考察点 — async & await 实现 asyncAdd 具体实现 进行优化 抽离内层函数 缓存计算结果 前言 在掘金上发现一道既简单但个人觉得还挺有意思的一道题,题目如下: // 异步加法 function asyncAdd(a,b,cb){ setTimeout(() => { cb(null, a + b) }, Math.random() * 1000) } as

  • JavaScript手写call,apply,bind方法

    目录 前言 改写this实现思路 前期准备 手写call方法 手写apply方法 手写bind方法 前言 改变this指向在书写业务的时候经常遇到,我们经常采用以下方法进行改写 使用作用声明变量存储this 使用jJavaScript的原生方法call,apply,以及bind进行改写 第一种方法就不说了,就是一个变量存储的问题,主要说第二种如何实现的 call,bind,apply方法都是JavaScript原生的方法,挂载在Function原型上,使得所有函数都可以调用,今天我们来实现一下c

  • JavaScript实现与web通信的方法详解

    web通信,一个特别大的topic,涉及面也是很广的.因最近学习了 javascript 中一些 web 通信知识,在这里总结下.文中应该会有理解错误或者表述不清晰的地方,还望斧正! 一.前言 1. comet技术 浏览器作为 Web 应用的前台,自身的处理功能比较有限.浏览器的发展需要客户端升级软件,同时由于客户端浏览器软件的多样性,在某种意义上,也影响了浏览器新技术的推广.在 Web 应用中,浏览器的主要工作是发送请求.解析服务器返回的信息以不同的风格显示.AJAX 是浏览器技术发展的成果,

  • javascript读取本地文件和目录方法详解

    JavaScript是网页制作中离不开的脚本语言,依靠它,一个网页的内容才生动活泼.富有朝气.但也许你还没有发现并应用它的一些更高级的功能吧?比如,对文件和文件夹进行读.写和删除,就象在VB.VC等高级语言中经常做的工作一样.怎么样,你是否需要了解这方面的知识?那就请跟我来,本文将详细描述如何使用Javascript语言进行文件操作. 一.功能实现核心:FileSystemObject 对象 其实,要在Javascript中实现文件操作功能,主要就是依靠FileSystemobject对象.在详

  • C语言实现手写JSON解析的方法详解

    目录 什么是JSON JSON支持的数据类型 JSON语法规则 JSON的解析 JSON基本语法 编写解析器 头文件 实现文件 什么是JSON JSON(JavaScript Object Notation)是一种轻量级的数据交换格式,用来传输属性值或者序列性的值组成的数据对象. JSON是JavaScript的一个子集. 具有良好的可读性和便于快速编写的特性. JSON是独立于语言的文本格式,并且采用了类似C语言家族的一些习惯. JSON数据格式与语言无关,是目前网络中主流的数据传输格式之一,

  • Java实现BP神经网络MNIST手写数字识别的示例详解

    目录 一.神经网络的构建 二.系统架构 服务器 客户端 采用MVC架构 一.神经网络的构建 (1):构建神经网络层次结构 由训练集数据可知,手写输入的数据维数为784维,而对应的输出结果为分别为0-9的10个数字,所以根据训练集的数据可知,在构建的神经网络的输入层的神经元的节点个数为784个,而对应的输出层的神经元个数为10个.隐层可选择单层或多层. (2):确定隐层中的神经元的个数 因为对于隐层的神经元个数的确定目前还没有什么比较完美的解决方案,所以对此经过自己查阅书籍和上网查阅资料,有以下的

  • JavaScript 中有关数组对象的方法(详解)

    JS 处理数组多种方法 js 中的数据类型分为两大类:原始类型和对象类型. 原始类型包括:数值.字符串.布尔值.null.undefined 对象类型包括:对象即是属性的集合,当然这里又两个特殊的对象----函数(js中的一等对象).数组(键值的有序集合). 数组元素的添加 arrayObj.push([item1 [item2 [. . . [itemN ]]]]); 将一个或多个新元素添加到数组结尾,并返回数组新长度 arrayObj.unshift([item1 [item2 [. . .

  • Javascript中的迭代、归并方法详解

    迭代方法 在Javascript中迭代方法个人觉得尤为重要,在很多时候都会有实际上的需求,javascript提供了5个迭代方法来供我们操作,它们分别为: every() 对数组中的每一个项运用给定的函数,如果每项都返回true,那么就会返回true filter() 对数组中的每一个项运用给定的函数,把返回true的项组成一个新数组并返回 forEach() 对数组中的每一项运用给定的函数,但是没有任何的返回值 map() 对数组中的每一个项运用给定的函数并返回每次函数调用的结果组成新的数组

  • JavaScript中Number对象的toFixed() 方法详解

    定义和用法 toFixed() 方法可把 Number 四舍五入为指定小数位数的数字. 语法 NumberObject.toFixed(num) 参数 描述 num 必需.规定小数的位数,是 0 ~ 20 之间的值,包括 0 和 20,有些实现可以支持更大的数值范围.如果省略了该参数,将用 0 代替. 返回值 返回 NumberObject 的字符串表示,不采用指数计数法,小数点后有固定的 num 位数字.如果必要,该数字会被舍入,也可以用 0 补足,以便它达到指定的长度.如果 num 大于 l

  • JavaScript中关键字 in 的使用方法详解

    for-in循环应该用在非数组对象的遍历上,使用for-in进行循环也被称为"枚举". 对于数组 ,迭代出来的是数组元素 但不推荐,因为不能保证顺序,而且如果在Array的原型上添加了属性,这个属性也会被遍历出来,所以 最好数组使用正常的for循环,对象使用for-in循环 对于对象 ,迭代出来的是对象的属性: var obj = { "key1":"value1", "key2":"value2", &q

随机推荐