前言
學會了異步 promise
來來研究Generators
傳統方法
ES6 誕生以前,不同步驟的方法,大致有下面四種。
- 回調函數
- 事件監聽
- 發布/訂閱
- 承諾對象
Generator 函數將 JavaScript 異步編程帶入了一個全新的階段。
回調函數
就是大家所稱的 CallBack
JavaScript 對異常編寫程序的實際發現,就是回調函數。
所稱回調函數,就是把任務的第二段單獨寫在一個函數裡,等到重新執行進行這個任務的時候,就直接調用這個函數。
1 | fs.readFile('/etc/passwd', 'utf-8', function (err, data) { |
Promise
如果想深入了解可以去之前 的地方看看:)
以上都是比較常用的異步解決方法,之後還有
async/await
接下來要研究今天的主角Generator
Generator
執行 Generator
函數會返回一個遍歷器Obj對象
,可以依次遍歷 Generator 函數內部的每一個狀態
形式上,Generator函數是一個普通函數,但是有兩個特徵:
function
關鍵字與函數名之間有一個星號
- 函數體內部使用
yield
表達式,定義不同的內部狀態
1 | function* helloWorldGenerator() { |
使用方式
Generator
會返回一個遍歷器對象,即具有Symbol.iterator
屬性,並且返回給自己
1 | function* gen(){ |
通過
yield
關鍵字可以暫停generator
函數返回的遍歷器對象
的狀態
1 | function* helloWorldGenerator() { |
上述存在三個狀態:hello
、world
、return
需要透過next
方法,來取到它的內部狀態,運行邏輯如下:
- 遇到
yield
表達式,就暫停執行後面的操作,並將緊跟在yield
後面的那個表達式的值,作為返回的對象的value屬性值。 - 下一次調用
next
方法時,再繼續往下執行,直到遇到下一個yield
表達式 - 如果沒有再遇到新的
yield
表達式,就一直運行到函數結束,直到return
語句為止,並將return
語句後面的表達式的值,作為返回的對象的value
屬性值。 - 如果該函數沒有
return
語句,則返回的對象的value
屬性值為undefined
1 | hw.next() |
done
用來判斷是否存在下個狀態,value
對應狀態值
yield
表達式本身沒有返回值,或者說總是返回undefined
通過調用next
方法可以帶一個參數,該參數就會被當作上一個yield
表達式的返回值
1 | function* foo(x) { |
正因為Generator
函數返回Iterator
對象,因此我們還可以通過for...of
進行遍歷
1 | function* foo() { |
原生Obj
沒有遍歷接口
,通過Generator
函數為它加上這個接口,就能使用for...of
進行遍歷了
1 | function* objectEntries(obj) { |
異步的解決方案
回顧之前展開異步解決的方案:
- 回調函數 Callback
- Promise 對象
- generator 函數
- async/await
這裡通過文件讀取案例,將幾種解決異步的方案進行一個比較:
回調函數 CallBack
所謂回調函數 CallBack
,就是把任務的第二段單獨寫在一個函數里面,等到重新執行這個任務的時候,再調用這個函數
1 | fs.readFile('/etc/fstab', function (err, data) { |
readFile
函數的第三個參數,就是回調函數,等到操作系統返回了/etc/passwd
這個文件以後,回調函數 CallBack
才會執行
Promise 對象
Promise
就是為了解決回調地獄而產生的,將回調函數的嵌套,改成鍊式調用
1 | const fs = require('fs'); |
這種鍊式操作形式,使
異步任務
的兩段執行更清楚了,但是也存在了很明顯的問題,代碼變得冗雜了,語義化並不強
generator 函數
yield
表達式可以暫停函數執行,next方法
用於恢復函數執行,這使得Generator
函數非常適合將異步任務同步化
1 | const gen = function* () { |
async/await
將上面Generator函數改成async/await形式,更為簡潔,語義化更強了
1 | const asyncReadFile = async function () { |
區別
通過上述代碼進行分析,將promise
、Generator
、async/await
進行比較:
promise
和async/await
是專門用於處理異步操作的Generator
並不是為異步而設計出來的,它還有其他功能(對象迭代、控制輸出、部署Interator接口…)promise
編寫代碼相比Generator
、async更為複雜化,且可讀性也稍差Generator
、async
需要與promise
對象搭配處理異步情況async
實質是Generator
的語法糖,相當於會自動執行Generator
函數async
使用上更為簡潔,將異步代碼以同步的形式進行編寫,是處理異步編程的最終方案
使用場景
Generator
是異步解決的一種方案,最大特點則是將異步操作同步化表達出來
1 | function* loadUI() { |
包括
redux-saga
中間件也充分利用了Generator
特性
1 | import { call, put, takeEvery, takeLatest } from 'redux-saga/effects' |
還能利用
Generator
函數,在對像上實現Iterator
接口
1 | function* iterEntries(obj) { |