先看一个async/await 范例
const delayer = t => new Promise(resolve => setTimeout(resolve, t)) async function start() { console.log(1) await delayer(1000) console.log(2) await delayer(1000) console.log(3) }
以上代码会每隔1s输出一个数字。 那async 和 await 为何如此神奇,能把异步的代码写的看起来像同步的代码?
Generator 函数是 ES6 提供的一种异步编程解决方案,也是刚刚接触的同学难以理解的点之一, 当然这里也会先用一些简单的范例做引导便于大家去理解
先看一个范例:
function fn(a,b){ console.log('fn..') return a + b } function* gen(x) { console.log(x) let y = yield fn(x,100) + 3 console.log(y) return 200 }
上述声明了一个普通函数 fn,和一个 Generator 函数 gen,先执行如下代码
let g = gen(1)
调用Generator 函数,返回一个存储状态对象的引用,这个时候 gen 这个函数是没执行的,所以当你执行上面这行代码不会有任何输出
console.log( g.next() )
当调用g.next()
时,gen 函数开始执行,执行到第一个yield 为止,并把 yield 表达式的值作为状态对象的值。更具体一点,上例先输出x
也就是1
,然后执行 fn(x, 100)
输出 fn..
并返回101, 然后加3。这时候停止执行,把结果103赋值给状态对象 g,g 的结果变 {value: 103, done: false}。需要注意,yied表达式的优先级极其低,yield fn(x,100) + 3
相当于 yield (fn(x,100) + 3)
console.log( g.next() )
这次执行g.next()
的时候,代码由上次暂停处开始执行,但此时 yield 表达式的值并不是使用刚刚计算的结果,而是使用 g.next
的参数undefined
, 所以 y的值变为undefined
,输出undeined
。执行到return 200
时,状态对象知道执行结束了,会把return的200赋值到状态对象,结果为 { value: 200, done: true }
有同学会问,如何把刚刚计算的中间值103给下个yield来用呢?好问题,我们可以这样
let g = gen(1) g.next(g.next().value)
面做个个简单的优化,让Generator自动调用,知道状态变为done,原理大家自己好好想想
function run(gFun, ...initValues) { let gen = gFun(...initValues) function next(data) { let result = gen.next(data) if (result.done) { console.log(result) return } next(result.value) } next() } run(gen, 1) function fn(a,b){ console.log('fn..') return a + b } function* gen(x) { console.log(x) let y = yield fn(x,100) + 3 console.log(y) return 200 }
测试一下一开始的例子
function run(gFun, ...initValues) { let gen = gFun(...initValues) function next(data) { let result = gen.next(data) if (result.done) return result.value.then(data => next(data)) } next() } const delayer = t => new Promise(resolve => setTimeout(resolve, t)) function* start() { console.log(1) yield delayer(1000) console.log(2) yield delayer(1000) console.log(3) } run(start)
再加上错误处理
function run(gFun, ...initValues) { let gen = gFun(...initValues) function next(data) { return new Promise((resolve, reject) => { let result = gen.next(data) if (result.done) return result.value.then(data => next(data)).catch(e => reject(e)) }) } return next() } const delayer = t => new Promise((resolve, reject) => { setTimeout(() => { if(Math.random() > 0.5) resolve(t) else reject('wrong') }, t) }) function* start() { console.log('start') let t = yield delayer(1000) console.log(t) let t2 = yield delayer(2000) console.log(t2) } run(start).catch(err => console.log(err))
基本上和async / await 语法一致了。