Generator模拟实现async+await

# 简介 `ES6` 之前异步编程方法大概有4种,分别是回调函数、事件监听、发布订阅、`Promise` 对象。`ES6` 提供了 `Generator` 函数作为一种新的异步编程方案,`ES7` 中的 `async` 函数更是被作为异步编程的终极解决方案。 既然 `ES6` 已经提供了 `Generator` 函数来解决异步,为什么 `ES7` 又新增了 `async` 函数呢?其实 `async` 只是一个语法糖,其本质是 `Generator` 函数+执行器。 # 实现 当调用 `Generator` 函数时,会返回一个遍历器对象,代表 `Generator` 函数的内容指针。之后每次调用 `next` 方法时,就会返回一个有着 `value` 和 `done` 两个属性的对象。`value` 属性表示当前内部状态的值(对应`yield` 语句后的值),`done` 属性表示是否遍历结束。 基于 `Generator` 函数的特点,如果我们可以实现一个执行器,自动完成 `Generator` 函数的调用和遍历器对象的迭代执行,就可以实现类似 `async` 函数的效果。 以下是一个简单的 async 函数的代码,最终会输出 `AB`。 ```javascript async function asyncFun() { let a = await Promise.resolve('A') let b = await Promise.resolve('B') return a + b } asyncFun().then(res => console.log(res)) // AB ``` 使用 `Generator` 函数+执行器后,期望的效果如下: ```javascript function* generatorFun() { let a = yield Promise.resolve('A') let b = yield Promise.resolve('B') return a + b } // run 为执行函数 run(generatorFun).then(res => console.log(res)) // AB ``` `run` 函数的入参是 `Generator` 函数,返回值为 `Promise` 对象。核心逻辑是,迭代执行 `next` 函数,直到迭代完成(`done` 为 `true`)。简易实现为: ```javascript function run(gen) { return new Promise(function (resolve) { // 执行Generator函数 let g = gen() const next = (context) => { let { done, value } = g.next(context) if (done) { // 完成返回 resolve(value) } else { // 未完成继续执行next函数,并传入本次执行结果 value.then(next) } } next() }) } ``` 除了执行器之外,`async` 相比于 `Promise` 还有另一个特点,`Promise` 的异常一般通过 `catch` 函数来捕获,而 `async` 函数可以用 `try catch` 来捕获。而要实现这个效果,需要借助遍历器对象的 `throw` 方法。核心代码实现如下: ```javascript function run(gen) { return new Promise(function (resolve, reject) { let g = gen() const next = (context) => { let res try { res = g.next(context) } catch (e) { return reject(e) } if (res.done) { resolve(res.value) } else { res.value.then(next, err => { let res try { // 借助迭代器的throw方法抛出异常,可悲 try catch 捕获 res = g.throw(err) } catch (e) { // 如果外层没有try catch,依然可以通过catch方法捕获 return reject(e) } next(res) }) } } next() }) } function* generatorFun1() { try { let a = yield Promise.resolve('A') let b = yield Promise.reject('B') return a + b } catch (e) { console.error('try catch error', e) } } // 输出try catch error run(generatorFun1) .then(res => console.log(res)) .catch(err => console.error('promise catch', err)) function* generatorFun2() { let a = yield Promise.resolve('A') let b = yield Promise.reject('B') return a + b } // 输出promise catch run(generatorFun2) .then(res => console.log(res)) .catch(err => console.error('promise catch', err)) ``` # co 模块 上文代码仅为简易的实现,主要是为了了解 `Generator` 实现 `async` 的核心原理,并没考虑参数类型以及执行结果非 `Promise` 对象等问题。完善的执行器代码可以参考 [co 模块](https://github.com/tj/co/blob/master/index.js)的实现。 > `co` 模块是著名程序员 `TJ Holowaychuk` 于 2013 年 6 月发布的一个小工具,用于 `Generator` 函数的自动执行。 ```javascript // co与run一样都为generator函数执行器 var co = require('co'); co(generatorFun).then(res => console.log(res)) // AB ``` ​ ​
上一篇:Mybatis逆向生成报错:.\mbg.xml (系统找不到指定的文件)


下一篇:nodejs连接mysql数据库,报错Client does not support authentication protocol requested by server的解决方法