# 简介
`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
```
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 [email protected] 举报,一经查实,本站将立刻删除。