以前聊了異步編程的回調函數和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
由於一個好朋友的一道面試題,忽然有了寫一下關於同步、異步編程的一篇文章,寫起來才發現涉及的知識點太雜,就一共寫了三篇文章: