趁热打铁,写一个丐版的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()
        })
    }
}复制代码
相关文章
相关标签/搜索