众所周知:async+ await 就是 generator+自动执行器的语法糖
想要了解async/await就要先了解generator和thunk
generator
- 创建一个generator函数
//方法1
function* foo(x) {
yield x + 1;
yield x + 2;
return x + 3;
}
//方法2
function *foo(x) {
yield x + 1;
yield x + 2;
return x + 3;
}
-
generator函数的特点
相比于普通函数,除了 return,还可以使用yield 返回多次 -
运行generator函数
//方法一:使用generator函数的next函数
const fn = foo(5) //创建了一个generator函数
fn.next() //{value:6,done:false},调用next,直至遇见return,返回的yield语句后的值
...
fn.next() //{value: undefined, done:true}//
//方法2 使用for/of
for(let i of foo(5)){
// console.log(i) //for of 遍历
}
插播for/of 和 for/in的区别
- for in 适合对象(索引,不按顺序,遍历所有可枚举属性包括原型对象,包括数组上的方法)
- for of 适合数组(值)
- next()函数
- 第一个 next()用于启动生成器,传入参数无效,此时遇到 yield 就会暂停
- 第 2 个 next()时,会启动第一个暂停的 yield,可以向其传递值,传递的值会替代第一个暂停的 yield 语句
若不传递,就不会替代 yield 语句,就不会给左边赋值,左边就会时 undefined
function* foo(x) {
let y = yield x + 1;
yield y+ 3;
return x + y;
}
//正常
const fn = foo(2)
fn.next() // 3
fn.next(2) // 5
fn.next() // 4
//异常
const fn = foo(2)
fn.next() // 3
fn.next() // y:undefined NaN
fn.next() // y:undefined NaN
- yield委托
- 原因
有时候需要将多个generator的值获取在一起,这时候就要使用yield* 委托到一个generator函数中
yield* 后面跟一个生成器函数、或其他可迭代的对象(如一个数组、字符串、arguments对象) - 执行
function* g1(){
yield 1
yield 2
return \'g1\'
}
function* g2(){
yield 3
yield 4
return \'g2\'
}
function g(){
yield* g1()
let str = yield* g2()
console.log(str)
}
const gen = g()
gen.next() //{value:1,done:false}
gen.next() //{value:2,done:false}
gen.next() //{value:3,done:false}
gen.next() //{value:4,done:false}
gen.next() //{value:undefined,done:true}
**
为什么没有获取到g1和g2函数中return的值呢?
yield* 的作用是将函数本身的yield表达式代理出去。故在执行的时候只代理了yield表达式,没代理return语句。
yield* 返回的结果就是代理的函数return的结果。
**
thunk 函数
- 编译器中的thunk
- 最初编译时出现了一个争论,即"求职策略”,
比如函数传参数,是“按值传递”,传递时即执行完毕,传入的是一个具体的值;“按名传递",等到具体要使用的时候在执行求值。“按名传递"比按值传递多了一个优点,即若是传入的值没有使用,则可以节省效率。
编译器中的 thunk,往往是将参数放入到一个临时函数中,在将函数传递整个函数题,这个临时函数就叫 thunk - js 中是按值传递的,js 中的 thunk 是用来处理多参数函数的,将多参数函数转变为参数为回调函数的单参数函数
- js中的thunk
如 readFile(‘/file.txt\',callback)//多参数函数
//thunk版本的
function myThunk(filename){
return function (callback){
func.readFile(\'/file.txt\',callback)
}
}
const newFunc = myThunk(\'./file.txt\')
newFunc(callback)
- 实现简单的 thunk 转化器
//thunk转化器
function myThunk(fn){
return function (){
let args = Array.prototype.slice.call(arguments)
return function(callback){
args.push(callback)
return fn.apply(this,args)
}
}
}
//转化readFile
var newRead = myThunk(readFile)
newRead(\'/file.txt\')(callback)
- thunk的作用
thunk 可以用作 generator 的流程管理
//普通流程管理
cont readFile = thunkify(fs.readFile) //转化为thunk函数
function* gen(){
let r1 = yield readFile(\'/file1.txt\')
console.log(r1)
let r2 = yield readFile(\'/file2.txt\')
console.log(r2)
}
const fn = gen()
const result = fn.next()
result.value((err,data)=>{
if(error) throw err
const res = fn.next(data)
res.value((err,data)=>{
if(error) throw err
const res = fn.next(data)
})
})
//更强大的自动流程管理
const fn = (func)=>{
var a = func()
const next = (err,data)=>{
if(err) throw err
const r1 = a.next(data)
if(r1.done){
return r1.value
}
r1.value(next)
}
next()
}
自动流程管理和async/await的流程管理逻辑基本一致,主要async/await主要针对于promise
async/await
- 基本信息
- async
会返回一个 promise 对象,并且 promise 的状态是 fulfilled,promise 中的 resolve 的值是函数 return 的值 - await
会阻塞后端的代码,先执行外部的代码,
await 后等一个 promise 会等 promise 状态变为 fulfilled 的,把 resolve 的结果 💺await 等待的结果
若 promise 的状态变为 reject 会抛出异常 可以用 try catch 捕获
- async await 实现
//异步函数
function getNumber(d){
return new Promise((resolve,reject)=>{
setTimeout(()=>{
console.log(d)
resolve(d)
},10000)
})
}
function asyncFn(func){
const g = func()
const next = (data)=>{
const result = g.next(data);
if(result.done){
return result.value
}
else{
result.value.then((sd,fd){
next(sd)
})
}
}
next()
}
const func = function * (){
const f1 = yield getNumber(1)
const f2 = yield getNumber(f1)
}
asyncFn(func);