趁熱打鐵,寫一個丐版的async await

以前一直是用Promise來解決異步請求數據的問題,後來無心中知道了ES8的async await,能把異步的代碼寫的像同步,就去研究了一下其原理是用了Generator,今天就來實現一個簡單的async awaitbash

什麼是Generator

個人理解是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複製代碼

封裝一個簡單的Generator

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()
    })
}複製代碼

優化幾個小問題

  1. 若是傳入的fn函數有參數
  2. this指向問題
  3. 直接return了一個new Promise彷佛是隻能使用Promise,萬一後面想更改咧,因此咱們給它包裝一層匿名函數

因此個人最終版代碼是:

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()
        })
    }
}複製代碼
相關文章
相關標籤/搜索