async 是Generator函数的语法糖,并对Generator函数进行了改进bash
async function fn(args) {
// ...
}
// 等同于
function fn(args) {
return spawn(function* () {
// ...
});
}
复制代码
spawn 函数就是自动执行器, 下面给出spawn函数的实现babel
function spawn(genF){
return new Promise((resolve, reject)=>{
const gen = genF() // 先将Generator函数执行下,拿到遍历器对象
function step(nextF) {
let next
try {
next = nextF()
} catch(e){
return reject(e)
}
if(next.done){
return resolve(next.value)
}
Promise.resolve(next.value).then((v)=>{
step(()=>{return gen.next(v)})
}, (e)=>{
step(()=>{return gen.throw(e)})
})
}
step(()=> {return gen.next(undefinex)})
})
}
复制代码
async 和 await, 比起星号和yield,语义更加清楚,async表示函数里面有异步操做,await表示紧跟在后面的表达式须要等待结果。多线程
co模块约定,yield命令后面只能是Thunk函数或者Promise对象, 而async函数的await后面,能够是Promise和原始类型值(数值、字符串和布尔值,但这时会自动转成当即 resolved 的 Promise 对象, 查看spawn函数中Promise.resolve(next.value))闭包
比Generator函数的返回值是Iterator对象方便,可使用then方法指定下一步操做异步
Generator函数语法:重点是*和关键字yieldasync
function* helloWorldGenerator() {
yield 'hello';
yield 'world';
return 'ending';
}
var hw = helloWorldGenerator();
复制代码
执行Generator函数helloWorldGenerator的话,并不执行,而是返回一个迭代器对象,下一步,必须调用遍历器对象的next方法,使得指针移向下一个状态函数
hw.next()
// { value: 'hello', done: false }
hw.next()
// { value: 'world', done: false }
hw.next()
// { value: 'ending', done: true }
hw.next()
// { value: undefined, done: true }
复制代码
yield 因为 Generator 函数返回的遍历器对象,只有调用next方法才会遍历下一个内部状态,因此其实提供了一种能够暂停执行的函数。yield表达式就是暂停标志。工具
协程是一种程序运行的方式,能够用单线程实现,也能够用多线程实现。ui
一个线程(或函数)执行到一半,能够暂停执行,将执行权交给另外一个线程(或函数),等到稍后收回执行权的时候,再恢复执行。这种能够并行执行、交换执行权的线程(或函数),就称为协程。spa
Generator 函数是 ES6 对协程的实现,可是不彻底,Generator 函数被称为“半协程”(semi-coroutine),意思是只有 Generator 函数的调用者,才能将程序的执行权还给 Generator 函数。若是是彻底执行的协程,任何函数均可以让暂停的协程继续执行。
Generator 函数, 它执行产生的上下文环境,一旦遇到yield命令,就会暂时退出堆栈,可是并不消失,里面的全部变量和对象会冻结在当前状态。等到对它执行next命令时,这个上下文环境又会从新加入调用栈,冻结的变量和对象恢复执行。
具体请参考alloyteam的文章
js的Generator并不是由引擎从底层提供额外的支持,而是经过代码的编写,来实现的函数暂停、按序执行。 在实现Generator过程当中有两个关键点,一是要保存函数的上下文信息,二是实现一个完善的迭代方法,使得多个 yield 表达式按序执行,从而实现生成器的特性。 大致上就是使用由 switch case 组成的状态机模型中, 除此以外,利用闭包技巧,保存生成器函数上下文信息。
Regenerator 经过工具函数将生成器函数包装,为其添加如 next/return 等方法。同时也对返回的生成器对象进行包装,使得对 next 等方法的调用,最终进入由 switch case 组成的状态机模型中。除此以外,利用闭包技巧,保存生成器函数上下文信息。
上述过程与 C#中 yield 关键字的实现原理基本一致,都采用了编译转换思路,运用状态机模型,同时保存函数上下文信息,最终实现了新的 yield 关键字带来的新的语言特性。
这个连接能够查看转换后的代码:这个在线地址