2015-06-06 104 views
7

我正在遷移現有程序以使用async/await(通過BabelbluebirdCoroutines)以瞭解此樣式。我一直在尋找這tutorialES7異步/等待概念問題

我對以下行爲有些困擾。這段代碼按預期工作:

let parts = []; 
let urlsP = urls.map((url, index) => { 
    return dlPart(url, index, tempDir); 
}); 
for (let urlP of urlsP) { // Parallel (yay!) 
    parts.push(await urlP); 
} 
for (let part of parts) { // Sequential 
    await appendFile(leFile, part); 
} 

重新編寫如下,它仍然有效,但拳頭操作不平行的話(它需要更長的時間才能完成)!

let index = 0; 
let parts = []; 
for (let url of urls) { // NOT Parallel any more!!! 
    index++; 
    parts.push(await dlPart(url, index, tempDir)); 
} 
for (let part of parts) { 
    await appendFile(leFile, part); 
} 

這是dlPart()

function dlPart(url, num, dir) { 
    var cmd = 'wget --quiet "' + url + '" -O ' + dir + "/" + num; 
    return exec(cmd).then(() => { 
     return dir + '/' + num; 
    }); 
} 

我缺少的是執行?

+1

迭代是同步的,從我的理解,它會在順序發生。它認爲'爲..循環提議旨在改善這一點,但它並未在任何地方實施。查看信息https://github.com/jhusain/asyncgenerator – elclanrs

回答

2

.map函數是異步的,所以其餘代碼不需要等待它,它會在準備就緒時完成。然後你用一個for loop代替它,它在完成時將所有東西都保存起來。

+0

謝謝!閱讀你的回答和@elclanrs評論讓我意識到它實際上是這樣說文章(用另一種措辭),但我設法忽略它的重要性: – rollingBalls

+1

@rollingBalls享受! – Datsik

+3

這不是for循環,它將所有內容都保存起來 - 它是在創建__ promises時很重要的 - 在地圖版本中,您預先創建了所有內容,而for循環版本則是一個一個地創建它們。請記住,承諾是一個_already started_操作(在你的情況下下載)。 –

4

它不再平行運行的原因是因爲您在兩個示例中創建承諾時的時機。這更清楚地描述了above comment

在您的第一個示例中,您將啓動所有開始執行其功能的Promise。然後,在這個循環:

for (let urlP of urlsP) { // Parallel (yay!) 
    parts.push(await urlP); 
} 

您等待第一個承諾要做,然後做第二承諾等,但要做的事情,你都在等待第一個應許的全部時間所有其他承諾仍在執行中。因此他們在「平行」運行。

在你的第二個例子中,你既是START又是AWAIT循環內的承諾,而不是在循環之前啓動它們。因此,在這個代碼:

for (let url of urls) { // NOT Parallel any more!!! 
    index++; 
    parts.push(await dlPart(url, index, tempDir)); 
} 

parts.push線也將爲了以下:

  1. 奔跑dlPart()返回一個承諾,並開始下載部分
  2. 等待承諾解決
  3. 將已解析的值推送到parts

所有其他的承諾還沒有開始,並沒有「並行」運行,他們只有在輪到他們時纔開始。這意味着它們一次只能調用一個,並且只有在前一個完成時才啓動下一個,這就是爲什麼它們迭代運行的原因。

注:.map is not asynchronous如果它那麼你的第一個例子不會有大名單的工作,因爲之前所有的承諾都添加到您的urlsP陣列for of循環將開始。

1

你可以更好地看到代碼之間的區別,當它的寫法略有不同時。

對於所有意圖和目的,這正是薩姆解釋,但在我找到一種方法,可以幫助開發人員瞭解它的方式,他們更習慣。

ES7代碼

let parts = []; 
let urlsP = urls.map((url, index) => { 
    return dlPart(url, index, tempDir); 
}); 
for (let urlP of urlsP) { // Parallel (yay!) 
    parts.push(await urlP); 
} 

ES6代碼

let parts = []; 
// Capture all the pending promises into an array 
let urlsP = urls.map((url,index)=>{ 
    // Returns the promise to the urlsP array 
    return dlPart(url,index,tempDir); 
}); 
// Catch all the data in an array 
Promise.all(urlsP).then(res=>{ 
    parts=res; 
}); 

重申一下山姆解釋上面的帖子。 在ES7例如在地圖功能的所有功能異步調用,並創建承諾的新的數組。 for of loop迭代通過承諾數組並檢查,看看承諾是否已經解決,如果沒有,它會等待,直到那個特定的承諾解決,然後重複這個過程。如果你能看在與鉻調試器標籤,你會發現,有些承諾會由當時的循環檢查,看它是否得到了解決解決慢動作這段代碼,而其他人,你將不得不等待

的ES6例子在本質上是一樣的,在我們如何得到零件組列的唯一區別。在這種情況下,承諾所有的反應是所有解析值的數組,因此,我們使零件等於響應,而不是推入陣


現在ES6想象寫了下面的代碼:

ES7代碼

let index = 0; let parts = []; 
for (let url of urls) { // NOT Parallel any more!!! 
    index++; 
    parts.push(await dlPart(url, index, tempDir)); 
} 

你將不得不使用發電機或遞歸函數,我的發電機的認識還很新,所以我將展示一個遞歸函數

ES5/6碼

let index = 0; let parts = []; let promises = []; 

function getParts(index,){ 
     return new Promise((resolve,reject)=>{ 
      dlPart(urls[index],index,tempDir).then(res=>{ 
       parts.push(res) 
       if(index<urls.length-1){ 
        promises.push(getParts(++index)); 
        resolve() 
       }else{ 
        resolve() 
       } 
      } 
     } 
    } 
promises.push(getParts(index)); 
Promise.all(promises).then(finished=>{ 
    // Then you can continue with whatever code 
}); 

與此ES7例如上面的代碼,你會發現,for of loop遍歷的URL陣列並等待承諾移動到下一個之前解決現在數組的索引。

的ES6例子確實在某種意義上說,它會與索引0處的URL開始一樣,等待dlPart承諾來解決,推動響應入零件組列,checkes索引是更小的比的URL數組長度的getParts然後再次調用自身,直到最後完全耗盡網址索引並解決其最後的承諾,以使Promise.all(promises)下面的代碼可以開始運行

當你開始尋找在差異ES6和ES7之間的可讀性可以看出爲什麼異步/等待在es7中完成佩奇。