[譯] JavaScript - Generator-Yield/Next 和 Async-Await

iceytea發表於2019-03-04

[譯] JavaScript - Generator-Yield/Next 和 Async-Await

Generator (ES6)

generator 函式是一個可以根據使用者需求,在不同的時間間隔返回多個值,並能管理其內部狀態的函式。如果一個函式使用了 function* 語法,那麼它就變成了一個 generator 函式。

它們與正常函式不同,正常函式在單次執行中完成執行,而 generator 函式可以被暫停和恢復。它們確實會執行完成,但觸發器在我們手中。它們使得對非同步函式能有更好的執行控制,但這並不意味著它們不能用作同步函式。

注意:執行 generator 函式時,會返回一個新的 Generator 物件。

generator 的暫停和恢復是使用 yieldnext 完成的。讓我們來看看它們是什麼,以及它們能做什麼。

Yield/Next

yield 關鍵字暫停 generator 函式的執行,並且 yield 關鍵字後面的表示式的值將返回給 generator 的呼叫者。它可以被理解為基於 generator 版本的 return 關鍵字。

yield 關鍵字實際上返回一個具有 valuedone 兩個屬性的 IteratorResult 物件。(如果你不瞭解什麼是 iterators 和 iterables,點選這裡閱讀)。

一旦暫停 yield 表示式,generator 的程式碼執行將保持暫停狀態,直到呼叫 generator 的 next() 方法為止。每次呼叫 generator 的 next() 方法時,generator 都會恢復執行並返回 iterator 結果。

嗯……理論先到這裡,讓我們看一個例子:

function* UUIDGenerator() {
    let d, r;
    while(true) {
        yield 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function(c) {
            r = (new Date().getTime() + Math.random()*16)%16 | 0;
            d = Math.floor(d/16);
            return (c=='x' ? r : (r&0x3|0x8)).toString(16);
        });
    }
};
複製程式碼

UUIDGenerator 是一個 generator 函式,它使用當前時間和隨機數計算 UUID ,並在每次執行時返回一個新的 UUID 。

要執行上面的函式,我們需要建立一個可以呼叫 next() 的 generator 物件:

const UUID = UUIDGenerator();
// UUID is our generator object

UUID.next() 
// return {value: 'e35834ae-8694-4e16-8352-6d2368b3ccbf', done: false}
複製程式碼

每次 UUID.next() 返回值的 value 值是新的 UUID ,done 值將始終為 false ,因為我們處於一個無限迴圈中。

注意:我們在無限迴圈上暫停,這是一種很酷的方式。在 generator 函式中的任何“停止點”處,不僅可以為外部函式生成值,還可以從外部接收值。

有許多 generator 的實現,並且很多庫都在大量使用。比如說 cokoaredux-saga


Async/Await (ES7)

[譯] JavaScript - Generator-Yield/Next 和 Async-Await

依照慣例,當一個非同步操作返回由 Promise 處理的資料時,回撥會被傳遞並呼叫。

Async/Await 是一種特殊的語法,以更舒適的方式使用 Promise,這種方式非常容易理解和使用。

Async 關鍵字用於定義非同步函式 ,該函式返回一個 AsyncFunction 物件。

Await 關鍵字用於暫停非同步函式執行,直到 Promise 被解決(resolved 或者 rejected),並在完成後繼續執行 async 函式。恢復時,await 表示式的值是已執行的 Promise 的值。

關鍵點:

  1. Await 只能在非同步函式中使用。
  2. 具有 async 關鍵字的函式將始終返回 promise。
  3. 在相同函式下的多個 await 語句將始終按順序執行。
  4. 如果 promise 正常被 resolve,則 await 會返回 promise 結果。但是如果被 reject,它就會丟擲錯誤,就像在那行有 throw 語句一樣。
  5. 非同步函式不能同時等待多個 promise。
  6. 如果在 await 之後使用 await 多次,並且後一條語句不依賴於前一條語句,則可能會出現效能問題。

到目前為止一切順利,現在讓我們看一個簡單的例子:

async function asyncFunction() {

  const promise = new Promise((resolve, reject) => {
    setTimeout(() => resolve("i am resolved!"), 1000)
  });

  const result = await promise; 
  // wait till the promise resolves (*)

  console.log(result); // "i am resolved!"
}

asyncFunction();
複製程式碼

await promise 這一行,asyncFunction 執行“暫停”,並在 promise 被解決後回覆,result(第 95 行的 const result)變成它的結果。上面的程式碼在一秒鐘後展示 “i am resolved!”。


Generator 和 Async-await 比較

  1. Generator 函式/yieldAsync 函式/await 都可以用來編寫“等待”的非同步程式碼,這意味著程式碼看起來像是同步的,即使它確實是非同步的。
  2. Generator 函式按照 yield 接著 yield 的順序執行,就是說一個 yield 表示式通過迭代器來執行一次(執行 next 方法),而 Async-await 按照 await 接著 await 的順序依序執行。
  3. Async/await 可以更容易地實現 Generators 的特定用例。
  4. Generator 的返回值始終是 {value: X, done: Boolean}。對於 Async 函式它將始終是一個將解析為值 X 或丟擲錯誤的 promise
  5. Async 函式可以分解為 Generator 和 promise 來實現,這些都很有用。

如果您想要新增到我的電子郵件列表中,請考慮 在此處輸入您的電子郵件,並在 medium 上關注我以閱讀更多有關 javascript 的文章,並在 github 上檢視我的瘋狂程式碼。如果有什麼不清楚的,或者你想指出什麼,請在下面評論。

你可能也喜歡我的其他文章:

  1. Nodejs app structure
  2. Javascript data structure with map, reduce, filter
  3. Javascript- Currying VS Partial Application
  4. Javascript ES6 — Iterables and Iterators
  5. Javascript performance test — for vs for each vs (map, reduce, filter, find).
  6. Javascript — Proxy

如果你喜歡這篇文章,請鼓掌。提示:你可以拍 50 次!此外,歡迎推薦和分享,以幫助其他人找到它!

謝謝!

如果發現譯文存在錯誤或其他需要改進的地方,歡迎到 掘金翻譯計劃 對譯文進行修改並 PR,也可獲得相應獎勵積分。文章開頭的 本文永久連結 即為本文在 GitHub 上的 MarkDown 連結。


掘金翻譯計劃 是一個翻譯優質網際網路技術文章的社群,文章來源為 掘金 上的英文分享文章。內容覆蓋 AndroidiOS前端後端區塊鏈產品設計人工智慧等領域,想要檢視更多優質譯文請持續關注 掘金翻譯計劃官方微博知乎專欄

相關文章