大厂技术 高级前端 Node进阶
点击上方 程序员成长指北,关注公众号
回复1,加入高级Node交流群
前言
在掘金上发现一道既简单但个人觉得还挺有意思的一道题,题目如下:
// 异步加法
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)
}
复制代码
从上述内容来看,最明显的就是 setTimeout
和 cb
了,其实这不难理解因为在 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
异步函数,**可点击此处查看详细内容**[2]。
一句话:sum
需要返回 promise
类型的值,即 sum
一定会使用到 promise
,并且从 sum(1,2,3,4,5,6,4)
可知 sum
可接收任意长度的参数。
实现 asyncAdd
题目的实现方式并不唯一,但核心点仍旧是考察你对 JavaScript
异步操作方面的思考和实现,以下只是给出了一种直观的解决方式,算是 抛砖引玉,可以将你的最优解贡献在评论区!
具体实现
实现思路如下:
考虑到外部参数长度不固定,使用剩余运算符接收所有传入的参数 考虑到 asyncAdd
中的异步操作,将其封装为Promise
的实现,即caculate
函数考虑到 asyncAdd
实际只能一次接收两个数字进行计算,使用循环的形式将多个参数分别传入考虑到通过循环处理异步操作的顺序问题,使用 async/await
来保证正确的执行顺序,且async
函数的返回值正好符合sum
是Promise
类型的要求
具体代码如下:
// 通过 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);
});
})
}
复制代码
关于本文
作者:熊的猫
https://juejin.cn/post/7134583899597832200
Node 社群
我组建了一个氛围特别好的 Node.js 社群,里面有很多 Node.js小伙伴,如果你对Node.js学习感兴趣的话(后续有计划也可以),我们可以一起进行Node.js相关的交流、学习、共建。下方加 考拉 好友回复「Node」即可。
如果你觉得这篇内容对你有帮助,我想请你帮我2个小忙:
1. 点个「在看」,让更多人也能看到这篇文章 2. 订阅官方博客 www.inode.club 让我们一起成长 点赞和在看就是最大的支持❤️
来源: 程序员成长指北