说到async得先说下Generator函数
Generator
Generator 函数是 ES6 提供的一种异步编程解决方案
function* helloWorld() {
yield 'hello'
yield 'world'
return 'ending'
}
let hw = helloWorld()
执行结果
console.log(hw.next()) //打印 {value: "hello", done: false}
console.log(hw.next()) //打印 {value: "world", done: false}
console.log(hw.next()) //打印 {value: "ending", done: true}
console.log(hw.next()) //打印 {value: undefined, done: true}
Generator 函数,返回一个遍历器对象,代表 Generator 函数的内部指针。每次调用遍历器对象的next方法,就会返回一个有着value和done两个属性的对象。value属性表示当前的内部状态的值,是yield表达式后面那个表达式的值;done属性是一个布尔值,表示是否遍历结束。
yield
Generator 函数如果是使用yield表达式,就变成了一个单纯的__暂缓执行函数__。
function* helloWorld() {
console.log('hello' + ' world')
}
let hw = helloWorld()
setTimeout(() => {
hw.next()
}, 1000)
//1秒后打印 hello world
next
yield表达式本身没有返回值,或者说总是返回undefined。next方法可以带一个参数,该参数就会被当作上一个yield表达式的返回值
function* f(x) {
var y = 2 * (yield (x + 1))
var z = yield (y / 3)
return (x + y + z)
}
let g = f(0);
//输出0(注意第一次使用next方法时,传递参数是无效的)
console.log(g.next())
//x等于0, y等于120(2*60)输出40 (120/3)
console.log(g.next(60))
//x等于0,y等于120, z等于200 输出320 (0 + 120 + 200)
console.log(g.next(200))
Generator 函数从暂停状态到恢复运行,它的上下文状态(context)是不变的。通过next方法的参数,就有办法在 Generator 函数开始运行之后,继续向函数体内部注入值。也就是说,可以在 Generator 函数运行的不同阶段,从外部向内部注入不同的值,从而调整函数行为。
异步执行
function* f() {
const data = yield (new Promise((resolve, reject) => {
setTimeout(() => {
resolve('hello async')
}, 1000)
}))
console.log('hello world')
}
let g = f()
const data = g.next()
data.value.then(data => {
return data
}).then(data => {
//data == hello async
g.next() //打印hello world
})
Generator 函数将异步操作表示得很简洁,但是流程管理却不方便(即何时执行第一阶段、何时执行第二阶段)。
Thunk 函数
Thunk 函数可以用于 Generator 函数的自动流程管理,下面的run方法就是一个Generator 函数的自动执行器
function* f() {
const f1 = yield (new Promise((resolve, reject) => {
setTimeout(() => {
resolve('hello async A')
}, 1000)
}))
const f2 = yield (new Promise((resolve, reject) => {
setTimeout(() => {
resolve('hello async B')
}, 1000)
}))
console.log(f1, f2)
}
function run(fn) {
let gen = fn()
function next(data) {
let result = gen.next(data)
if (result.done) return
result.value.then(data => {
next(data)
})
}
next()
}
run(f)
//输出 hello async A hello async B
co模块
co模块用于 Generator 函数的自动执行
const co = require('co')
function* f() {
const f1 = yield (new Promise((resolve, reject) => {
setTimeout(() => {
resolve('hello async A')
}, 1000)
}))
const f2 = yield (new Promise((resolve, reject) => {
setTimeout(() => {
resolve('hello async B')
}, 1000)
}))
console.log(f1, f2)
}
co(f).then(data => {
console.log('执行完成啦')
})
co 模块可以让你不用编写 Generator 函数的执行器,Generator 函数只要传入co函数,就会自动执行,co函数返回一个promise对象
co模块部分源码,其接收一个Generator函数作为参数
function co(gen) {
var ctx = this;
var args = slice.call(arguments, 1);
return new Promise(function(resolve, reject) {
if (typeof gen === 'function') gen = gen.apply(ctx, args);
if (!gen || typeof gen.next !== 'function') return resolve(gen);
onFulfilled();
function onFulfilled(res) {
var ret;
try {
ret = gen.next(res);
} catch (e) {
return reject(e);
}
next(ret);
return null;
}
function onRejected(err) {
var ret;
try {
ret = gen.throw(err);
} catch (e) {
return reject(e);
}
next(ret);
}
function next(ret) {
//ret.done 为true 表示遍历介绍 ret.value 为 Generator函数return返回的表达式,没有的话就是undefined
if (ret.done) return resolve(ret.value);
var value = toPromise.call(ctx, ret.value);
//调用 onFulfilled函数 执行下一个 next
if (value && isPromise(value)) return value.then(onFulfilled, onRejected);
//yield 后面 必须是 function, promise, generator, array, object数据类型
return onRejected(new TypeError('You may only yield a function, promise, generator, array, or object, '
+ 'but the following object was passed: "' + String(ret.value) + '"'));
}
})
}
async await
function* f() {
const f1 = yield (new Promise((resolve, reject) => {
setTimeout(() => {
resolve('hello async A')
}, 1000)
}))
const f2 = yield (new Promise((resolve, reject) => {
setTimeout(() => {
resolve('hello async B')
}, 1000)
}))
console.log(f1, f2)
}
async function g(){
const f1 = await (new Promise((resolve, reject) => {
setTimeout(() => {
resolve('hello async A')
}, 1000)
}))
const f2 = await (new Promise((resolve, reject) => {
setTimeout(() => {
resolve('hello async B')
}, 1000)
}))
console.log(f1, f2)
}
async函数就是将 Generator 函数的星号(*)替换成async,将yield替换成await,仅此而已
优点
1、内置执行器 Generator函数必须依赖执行器执行,比如co模块,async函数自带执行器,执行方式和普通函数一样
g()//打印 hello async A hello async B
2、更好的语义 async表示函数里有异步操作,await表示紧跟在后面的表达式需要等待结果
3、更广的适用性 4、返回值是promise