1

我需要從頭開始寫這個asyncMap函數。我想我已經差不多了,但我不確定爲什麼我一直得到錯誤的答案。這裏是我到目前爲止的代碼:如何從頭開始在Javascript中編寫異步Map函數?

function wait3For1(callback){ 
    setTimeout(function(){ 
     callback('one') 
    }, 300) 
} 

function wait2For5(callback){ 
    setTimeout(function(){ 
     callback('five') 
    }, 200) 
} 


function asyncMap(tasks, callback){ 
    return callback(
    tasks.map((item) => 
     item((element) => element))) 
} 

asyncMap([wait3For1, wait2For5], function(arr){ 
    console.log(arr) //expect ['one', 'five'] 
}); 

我不斷收到[undefined, undefined] 我敢肯定那是因爲我沒有做回調wait2For5和wait3For1正確的,但不知道是什麼問題。

在此先感謝!

+0

這是一個完美的承諾使用情況,它比使用回調有更好的錯誤捕獲 – charlietfl

+0

您的asyncMap是否假設平行或按順序做事情?換句話說,所有異步操作可以同時在運行嗎?還是您希望它們能夠一個接一個執行? – jfriend00

回答

1

問題是,您不會等待結果回來,收集它們,然後通過回調將它們發送回去。看看這段代碼是否有幫助。 (它的工作原理當你的程序進行測試。)

function asyncMap(tasks, callback) { 
    // array to collect the results 
    let results = []; 

    // count of how many results we're waiting for 
    let remaining = tasks.length; 

    tasks.forEach((task, i) => { 
     task((result) => { 
      // Store the result in the right position. 
      results[i] = result; 

      // See how many results we're still waiting for. 
      remaining -= 1; 

      // If we're done, invoke the callback. 
      if (remaining === 0) { 
       callback(results); 
      } 
     }); 
    }); 
} 
+0

如果不打算使用映射函數,函數應該被稱爲「asyncMap」以外的內容。 – naomik

+1

@naomik同意。 – smarx

0

你基本上是建立窮人的希望,但是沒有任何錯誤處理能力。

嘗試

function waitFor(val, dur){ 
 
     return new Promise(function(resolve, reject) {  
 
     setTimeout(function() { 
 
      resolve(val) 
 
     }, dur); 
 
     });  
 
    } 
 

 
    
 
    Promise.all([waitFor('one',600), waitFor('five', 100)]).then(function(arr) { 
 
     console.log(arr) //expect ['one', 'five'] 
 
    }).catch(function(err){ 
 
     console.log('ooops error:' , err) 
 
    });

1

在代碼中,您使用的同步Array.prototype.map

function asyncMap(tasks, callback){ 
    return callback(
    tasks.map((item) => 
     item((element) => element))) 
} 

由於wait3For1wait2For5沒有return,他們會隱返回undefined這將被用在的結果中打電話給。很明顯,我們希望在將映射值分配給最終結果之前等待回調被調用。

的另一個問題是,映射在一個陣列使用功能map(items)並沒有真正意義的無功能對項目進行映射。所以我們也會在下面的解決方案中解決這個問題。

如果您以asyncReduce開頭,然後將asyncMap作爲異步縮減開始,它將有所幫助。請注意,下面的代碼將處理系列中的項目。如果您希望並行處理項目,則需要稍微不同的方法。讓我知道在評論中,我會很樂意寫出另一個變體。

function wait3For1(callback){ 
 
    setTimeout(function(){ 
 
    callback('one') 
 
    }, 300) 
 
} 
 

 
function wait2For5(callback){ 
 
    setTimeout(function(){ 
 
    callback('five') 
 
    }, 200) 
 
} 
 

 
function asyncReduce(xs, f, initial, callback) { 
 
    if (xs.length === 0) 
 
    callback(null, initial) 
 
    else 
 
    f(initial, xs[0], function(x) { 
 
     asyncReduce(xs.slice(1), f, x, callback) 
 
    }) 
 
} 
 

 
function asyncMap(xs, f, callback) { 
 
    asyncReduce(xs, function(acc, x, k) { 
 
    f(x, function(y) { k(acc.concat([y])) }) 
 
    }, [], callback) 
 
} 
 

 
asyncMap([wait3For1, wait2For5], function(f,callback) { 
 
    f(callback) 
 
}, function(err, arr) { 
 
    console.log(arr) //=> ['one', 'five'] 
 
})

繼續運行的代碼片段,看看它的工作


我你使用一些箭頭的功能看,所以你可能有一個稍微近點的版本。這裏是完全相同的,但使用ES6代替

// ES6 
 
const wait3For1 = callback=> 
 
    setTimeout(callback, 300, 'one') 
 

 
const wait2For5 = callback=> 
 
    setTimeout(callback, 200, 'five') 
 

 
const asyncReduce = ([x,...xs], f, initial, callback) => 
 
    x === undefined 
 
    ? callback(null, initial) 
 
    : f(initial, x, y=> asyncReduce(xs, f, y, callback)) 
 

 
const asyncMap = (xs, f, callback)=> 
 
    asyncReduce(xs, (acc, x, k)=> 
 
    f(x, y=> k([...acc, y])) 
 
    , [], callback) 
 

 
asyncMap(
 
    [wait3For1, wait2For5], 
 
    (f, callback)=> f(callback), 
 
    (err, arr)=> console.log(arr) // ['one', 'five'] 
 
)


從頭手工藝高階函數是一個非常有價值的鍛鍊,但作爲另一個答案指出,你只是實施「窮人的Promise.all」,但多用途少得多。您應該將wait3For1wait2For5轉換爲Promise創作者,並使用Promise.all代替。

你的下一個問題可能是如何實施Promise.all ......我已經完成了這個最近,發現它是一個非常有趣的挑戰。我希望在這個答案中共享的一些技巧將有助於您選擇從頭開始探索一個實現^ _^