先貼代碼
const urls = torrentParser.urls() const peers = [] let count = 0 await new Promise( (resolve) => { urls.forEach( async (url) => { try { const res = await getPeers(url, torrentParser.infoHash(), torrentParser.size(), 15 * 1000) for (const peer of res.peers) { peers.push(peer) } } catch (e) { logger.error(e) } finally { count += 1 if ( count === urls.length ) { resolve() } } }) })
這段代碼要實現的邏輯是
我有很多urls,我想一下子訪問所有的urls,然后得到結果,這里的結果是peers的信息。
我不想用for loop,因為我的getPeers是寫成了同步函數的概念(要么返回結果,要么timeout),所以如果用for loop的話,就要一個一個url的訪問,一個一個url的blocking。
所以就用forEach了。但是我希望我接下來的邏輯是要等所有的request都完成了才進行。這里就又用到了promise。新建一個promise,然后await住它,就是要等它要么reject(),要么resolve()。這里沒有要拋出錯誤,就沒用reject,只用了resolve。等到所有的request都返回了,我就可以resolve了。一開始的方案是可以使用forEach的index參數,當index參數等於array的長度減一的時候就可以resolve這個promise了。但是后來發現因為有些request會有錯誤,會跑到catch中去。所以就引入了finally,在finally里面我自己累計下返回的request數量,當累計到了所有的request后,就resolve了。因為無論try還是catch都要執行finally的邏輯。
理解的核心還是nodejs是異步單線程的編程語言
即使感覺forEach好像是同時array里面所有的element就同時處理。(這里不是golang的協程,或者c++的多線程,它們是真的同時)。實際上,還是所有的element的處理是一個一個的處理,不同的是,不是處理完一個再來下一個,而是如果blocking了,就進行下一個,然后所有處理好的結果再排隊在一個callback的結果隊列中,一個一個的再被處理。

此話題在stack overflow有被討論
此代碼的完整代碼在這里
https://github.com/tw7613781/bittorrent/
