以前聊了异步编程的回调函数和promise,上一篇文章也说了,用promise解决异步编程,若是多个调用,就会看起来不那么舒服。ios
es6除了提供了promise还为咱们提供了更增强大的async和await,async、await是Generator函数的语法糖,若是想要彻底掌握async、await的用法,必需要掌握Generator函数的使用。es6
来自阮一峰老师文档上的解释:Generator函数是协程在 ES6 的实现,最大特色就是能够交出函数的执行权(即暂停执行)。面试
你能够这么理解,这个函数本身执行不了,得让别人帮忙执行,踢一脚(next()),走一步。编程
function* doSomething() {
yield '吃饭'
return '睡觉'
}
let newDoSomething = doSomething() // 本身执行不了,须要指向一个状态机
console.log(newDoSomething.next()) // {value: "吃饭", done: false}
console.log(newDoSomething.next()) // {value: "睡觉", done: true}
复制代码
一、function后面有个小*,这个地方有两种写法,没啥太大区别;axios
function* doSomething(){}
function *doSomething(){}
复制代码
二、函数里面会有一个yield,把函数截成不一样的状态;promise
一个yield能够截成两个状态,也就须要两个next()触发;
复制代码
三、Generator函数本身不会执行,而是会返回一个遍历器对象;微信
四、遍历器对象会经过.next()方法依次调用各个状态。markdown
Generator函数除了能控制函数分状态的执行,还有一个很是重要的做用就是消息传递,仍是上例子:异步
function *doSomething() {
let x = yield 'hhh'
console.log(x)
return (x * 2)
}
let newDoSomething = doSomething()
console.log(newDoSomething.next(1))
console.log(newDoSomething.next(2))
打印结果:
{value: "hhh", done: false}
2
{value: 4, done: true}
复制代码
具体分析一下为何会打印这个: (重点)async
//{value: "hhh", done: false}
第一个next()是Generator函数的启动器
这个时候打印的是yield后面的值
重点的一句,yield后面的值并不会赋值给x
//2
暂停执行的时候,yield表达式处能够接收下一个启动它的next(...)传进来的值
你能够理解为:
这个时候第二个next传入的参数会把第一个yield的值替换掉
//{value: 4, done: true}
这个时候,x被赋值2,因此打印2*2
复制代码
function *doSomething() {
let x = yield 'hhh'
let y = yield (x + 3)
let z = yield (y * 3)
return (x * 2)
}
let newDoSomething = doSomething()
console.log(newDoSomething.next(1)) // {value: "hhh", done: false}
console.log(newDoSomething.next(2)) // {value: 5, done: false}
console.log(newDoSomething.next(100)) // {value: 300, done: false}
console.log(newDoSomething.next(1000)) // {value: 4, done: true}
复制代码
仍是用上面的思路分析一下:
第一个next(1),传进去的值没用,打印的是yield后的值
第二个next(2),这个时候的x已经被赋值为2,因此打印2+3
第三个next(100),这个时候的y已经被赋值为100,因此打印100*3
第四个next(1000),这个时候y已经被赋值为1000,,可是打印的是x*2,因此打印的4
复制代码
function *doSomething() {
let x = yield 'hhh'
console.log(x)
let y = yield (x + 3)
console.log(y)
let z = yield (y * 3)
return (x * 2)
}
let newDoSomething = doSomething()
console.log(newDoSomething.next(1))
console.log(newDoSomething.next(2))
console.log(newDoSomething.next())
console.log(newDoSomething.next())
复制代码
看下打印结果:
{value: "hhh", done: false}
2
{value: 5, done: false}
undefined
{value: NaN, done: false}
{value: 4, done: true}
复制代码
分析下为何打印undefined?
一、第一个next(1)传进去的1,没有起任何意义,打印的{value: "hhh", done: false};
二、第二个next(2)传进去的2,因此x会打印2,第二个next(2)打印2+3;
三、第三个next()传进去的是空,那么y打印的就是未定义,undefined*3那确定就是NaN;
四、第四个next()传进去的是空,可是return的是x,刚才说了x是2,那打印的是2*2
复制代码
async、await是Generator函数的语法糖,原理是经过Generator函数加自动执行器来实现的,这就使得async、await跟普通函数同样了,不用再一直next执行了。
他吸取了Generator函数的优势,能够经过await来把函数分状态执行,可是又不用一直next,能够自动执行。
仍是上例子:
function f() {
return new Promise(resolve =>{
resolve('hhh')
})
}
async function doSomething1(){
let x = await f()
}
doSomething1()
打印结果:
hhh
复制代码
看了上面的例子,能够看出async有三个特色:
一、函数前面会加一个async修饰符,来证实这个函数是一个异步函数;
二、await 是个运算符,用于组成表达式,它会阻塞后面的代码
三、await 若是等到的是 Promise 对象,则获得其 resolve值。
复制代码
async function doSomething1(){
let x = await 'hhh'
return x
}
console.log(doSomething1())
doSomething1().then(res => {
console.log(res)
})
打印结果:
Promise {<pending>}
hhh
复制代码
分析一下上面的栗子能够获得这两个特色:
一、async返回的是一个promise对象,函数内部 return 返回的值,会成为 then 方法回调函数的参数;
二、await若是等到的不是promise对象,就获得一个表达式的运算结果。
复制代码
如今有一个封装好的,获取数据的方法,咱们分别用promise、Generator、async来实现发请求,作一下比较:
function getList() {
return new Promise((resolve, reject) =>{
$axios('/pt/getList').then(res => {
resolve(res)
}, err => {
reject(err)
})
})
}
复制代码
function initTable() {
getList().then(res => {
console.log(res)
}).catch(err => {
this.$message(err) // element的语法
})
}
复制代码
而后直接调用就能够 这么作看起来很是的简洁,可是若是多个请求调用 就会是.then,.then看起来很是不舒服
function *initTable(args) {
const getList = yield getlist(args)
return getList
}
function getList() {
const g = initTable(this.searchParams)
const gg = g.next().value
gg.then(res =>{
this.total = res.data.count
if (res.data.list) {
this.tableList = res.data.list
this.tableList.forEach(e => {
e.receiveAmt = format(e.receiveAmt)
})
} else {
this.tableList = []
}
})
}
复制代码
这个看起来就比较伤,写起来很是麻烦
async initTable() { // table列表查
const getData = await getList(this.searchParams)
return getData
},
getList() {
this.initTable().then(res =>{
this.tableList = res.data.list
})
}
复制代码
这样写好像也很简单,并且很是方便
主要是若是调用多个接口,能够直接多个await
由于一个好朋友的一道面试题,忽然有了写一下关于同步、异步编程的一篇文章,写起来才发现涉及的知识点太杂,就一共写了三篇文章: