本篇文章給大家帶來了關于javascript的相關知識,其中主要為大家介紹了JavaScript手寫異步加法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)
}從上述內容來看,最明顯的就是 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 異步函數,可點擊此處查看詳細內容。
一句話:sum 需要返回 promise 類型的值,即 sum 一定會使用到 promise,并且從 sum(1,2,3,4,5,6,4) 可知 sum 可接收任意長度的參數。
實現 asyncAdd
具體實現
實現思路如下:
考慮到外部參數長度不固定,使用剩余運算符接收所有傳入的參數
考慮到
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);
});
})
}





