以前一直是用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() }) } }复制代码