以前一直是用Promise來解決異步請求數據的問題,後來無心中知道了ES8的async await,能把異步的代碼寫的像同步,就去研究了一下其原理是用了Generator,今天就來實現一個簡單的async awaitbash
個人理解是Generator函數會返回一個迭代器對象,那麼先寫一個小案例來看下,markdown
function* my() { yield console.log(1) } var g = my() console.log(g)複製代碼
打印的結果是:app
能夠看到它有一個next方法,在咱們使用next方法的時候就會執行yield後面的語句,咱們打印一下g.next(),異步
正常執行了async
yield console.log(1)複製代碼
若是咱們有多個yield關鍵字的話,就得屢次調用next方法,函數
function* my() { yield console.log(1) yield console.log(2) yield console.log(3) yield console.log(4) } var g = my() console.log(g.next()) // 1 console.log(g.next()) // 2 console.log(g.next()) // 3 console.log(g.next()) // 4複製代碼
若是咱們多加一個優化
console.log(g.next())複製代碼
那麼這個時候結果就是,this
這個done就變成了true表示迭代完成,因此咱們能夠用它做爲遞歸的出口spa
let c = g.next() if (c.done) return複製代碼
1.封裝的話,確定是須要傳入一個函數做爲參數,因此初版的代碼以下(嗯,挺雞肋的)code
function createGen(fn) { var g = fn(), res function _next(val) { res = g.next() if (res.done) return res.value _next(res.value) } _next() }複製代碼
2.若是碰上Promise的話,上面封裝的函數就是死翹翹,因此咱們索性直接返回一個Promise,而後調用then方法,順便作一層try..catch的錯誤處理,因此代碼以下:
function createGen(fn) { return new Promise((resolve, reject) => { var g = fn(), res function _next(val) { // 由於是Promise的實例,因此咱們要作一層try...catch的包裹 try { // 拿到next執行後返回的對象,裏面有done和value兩個屬性 res = g.next(val) } catch (error) { reject(error) } // 若是迭代完成,那麼就結束,也就是咱們上面說的遞歸出口 if (res.done) return resolve(res.value) // 爲了防止res.value是一個原始值,咱們用Promise包裝一下,而後用then方法處理,成功了 // 就繼續調用_next方法去遞歸,直到res.done等於false Promise.resolve(res.value).then( v => _next(v), r => g.throw(r) ) } _next() }) }複製代碼
因此個人最終版代碼是:
function createGen(fn) { return function () { var self = this, res, g = fn.apply(self, arguments) return new Promise((resolve, reject) => { function _next(val) { // 由於是Promise的實例,因此咱們要作一層try...catch的包裹 try { // 拿到next執行後返回的對象,裏面有done和value兩個屬性 res = g.next(val) } catch (error) { reject(error) } // 若是迭代完成,那麼就結束,也就是咱們上面說的遞歸出口 if (res.done) return resolve(res.value) // 爲了防止res.value是一個原始值,咱們用Promise包裝一下,而後用then方法處理,成功了 // 就繼續調用_next方法去遞歸,直到res.done等於false Promise.resolve(res.value).then( v => _next(v), r => g.throw(r) ) } _next() }) } }複製代碼