这是我参与8月更文挑战的第28天,活动详情查看:8月更文挑战
这个系列也没啥花头,就是来整平时面试的一些手写函数,考这些简单实现的好处是能看出基本编码水平,且占用时间不长,更全面地看出你的代码实力如何。一般不会出有很多边界条件的问题,那样面试时间不够用,考察不全面。
平时被考到的 api 如果不知道或不清楚,直接问面试官就行, api 怎么用这些 Google 下谁都能马上了解的知识也看不出水平。关键是在实现过程,和你的编码状态、习惯、思路清晰程度等。
注意是简单实现,不是完整实现,重要的是概念清晰和实现思路清晰,建议
先解释清楚概念
=>写用例
=>写伪代码
=>再实现具体功能
,再优化
,一步步来。
34. 异步求和
题目
假设有一台本地机器,无法做加减乘除运算,因此无法执行 a + b、a += 1
这样的 js 代码,然后我们提供一个服务器端的 HTTP API,可以传两个数字类型的参数,响应结果是这两个参数的和,这个 HTTP API 的 JS SDK(在本地机器上运行)的使用方法如下:
asyncAdd(3, 5, (err, result) => {
console.log(result); // 8
});
其实就是把这个 加运算变成了需要异步
调用接口来获得和
我们为了调试,可以简单模拟实现这个异步接口
function asyncAdd(a, b, cb) {
setTimeout(() => {
cb(null, a + b);
}, Math.floor(Math.random() * 100))
}
你可以合并上面代码运行在浏览器试试, 成功得到 8 这个答案
那么问题就是需要你实现一个方法 sum
, 支持如下调用方式,并输出正确结果
(async () => {
const result1 = await sum(1, 4, 6, 9, 1, 4);
const result2 = await sum(3, 4, 9, 2, 5, 3, 2, 1, 7);
const result3 = await sum(1, 6, 0, 5);
console.log([result1, result2, result3]); // [25, 36, 12]
})()
分析
题目叙述完毕,我们了解了背景之后进行思考,sum
接受很多个参数,一个个加过去,但是又需要继续进行家和,而且是调用接口返回异步的结果作为和。想到 Promise
这个东西有 then
和进行链式调用 then().then().then()...
想到之前的 那个并发控制的实现吗,有点类似。
或者我们想到 async/await 同步方式编写异步,虽然底层还是 Promise
, 但代码更简单,下面来看代码注释
手写实现
其实核心就是用 promise 包装一层异步函数
function asyncAdd(a, b, cb) {
setTimeout(() => {
cb(null, a + b);
}, Math.floor(Math.random() * 100))
}
function promiseAsyncAdd(a, b) {
// 如果是 0 直接返回另一个数
if (a === 0) return b
// 用 Promise 包一层是要利用它能用 then进行链式调用
return new Promise((resolve, reject) => {
asyncAdd(a, b, (err, res) => {
resolve(res);
});
})
}
// 我们尝试着调用下这个函数, 没问题
// promiseAsyncAdd(1, 2).then(res => {
// console.log(res) // 3
// })
// 下面就是实现sum, 先用 then链式调用
function sum() {
let args = Array.from(arguments)
// 初始化一个 Promise, 和是 0
let p = Promise.resolve(0)
return args.reduce((acc, cur) => {
// 利用 promise 可链式调用
p = p.then(res => {
return promiseAsyncAdd(res, cur)
})
return p.then(res => {
// res 是每一轮 reduce计算的和 return出去
return res
})
}, 0)
}
(async () => {
const result1 = await sum(1, 4, 6, 9, 1, 4);
const result2 = await sum(3, 4, 9, 2, 5, 3, 2, 1, 7);
const result3 = await sum(1, 6, 0, 5);
console.log([result1, result2, result3]); // [25, 36, 12]
})()
// 成功输出 [25, 36, 12]
我们另外,写一下 async/await
写法
async function sum() {
const args = [...arguments];
let sum = 0
// 这里 i++ 也不给用 可以用 i--
for (let i = args.length - 1; i >= 0; i--) {
sum = await promiseAsyncAdd(sum, args[i]);
}
return sum;
}
那么8月最后一天更文,再加点料,提升效率
我们是希望这个执行时间更短,用
console.time
console.timeEnd
这两个 api 来测量一个 javascript 脚本程序执行消耗的时间。
// 启动计时器
console.time('testCodeTag');
// 需要计时的代码 标识 'testCodeTag' 之间的代码 比如下面的 for 循环执行时间
let res = []
for(let i = 0; i < 10000; i++) {
res.push(i * 5)
}
// 停止计时,输出时间
console.timeEnd('testCodeTag');
// 打印结果: testCodeTag: 0.792ms
接下来我们测量上面的代码效率, 先测 then
的
function asyncAdd(a, b, cb) {
setTimeout(() => {
cb(null, a + b);
}, Math.floor(50)) // 注意因为这个我们要计时就把它固定
}
(async () => {
console.time('testCodeTag');
const result1 = await sum(1, 4, 6, 9, 1, 4);
const result2 = await sum(3, 4, 9, 2, 5, 3, 2, 1, 7);
const result3 = await sum(1, 6, 0, 5);
console.log([result1, result2, result3]); // [25, 36, 12]
console.timeEnd('testCodeTag');
// testCodeTag: 895.751ms
})()
再测 async/await
的, 大概 896ms
两种差不多
我们思考有什么方式去更加缩短这个时间呢, 想到 Promise.all
是并发
的,会不会增加速度
这个具体写法我先来个思路,两两一组,合并成一次异步调用,我们现在有很多个异步调用,用Promise.all
来一波,之后进行下一轮再分组,all 一波直到最后剩一个和。
function asyncAdd(a, b, cb) {
setTimeout(() => {
cb(null, a + b);
}, Math.floor(50)) // 注意因为这个我们要计时就把它固定
}
function promiseAsyncAdd(a, b) {
// 如果是 0 直接返回另一个数
if (a === 0) return b
// 用 Promise 包一层是要利用它能用 then进行链式调用
return new Promise((resolve, reject) => {
asyncAdd(a, b, (err, res) => {
resolve(res);
});
})
}
// 拆分出来的并发方法
async function concurrentPromise(promises) {
return await Promise.all(promises).then(res => {
return res
})
}
async function sum() {
let res = 0
const args = [...arguments];
let promiseArr = []
// 两两分组
args.forEach(async (item, i) => {
if ((i+1) % 2 === 1) {
promiseArr.push(promiseAsyncAdd(item, args[i+1] || 0))
}
})
// 然后并发执行分组后的所有 promise
let endProRes = await concurrentPromise(promiseArr)
// 如果结果长度递归到只剩最后的一个和
if (endProRes.length > 1) {
res = sum(...endProRes)
} else {
res = endProRes[0]
}
return res;
}
(async () => {
console.time('testCodeTag');
const result1 = await sum(1, 4, 6, 9, 1, 4);
const result2 = await sum(3, 4, 9, 2, 5, 3, 2, 1, 7);
const result3 = await sum(1, 6, 0, 5);
console.log([result1, result2, result3]); // [25, 36, 12]
console.timeEnd('testCodeTag');
// testCodeTag: 487.948ms
})()
果然执行时间果然变短了,效率提升不少。
另外向大家着重推荐下另一个系列的文章,非常深入浅出,对前端进阶的同学非常有作用,墙裂推荐!!!核心概念和算法拆解系列 记得点赞哈
今天就到这儿,想跟我一起刷题的小伙伴可以加我微信哦 点击此处交个朋友
Or 搜索我的微信号infinity_9368
,可以聊天说地
加我暗号 "天王盖地虎" 下一句的英文
,验证消息请发给我
presious tower shock the rever monster
,我看到就通过,加了之后我会尽我所能帮你,但是注意提问方式,建议先看这篇文章:提问的智慧