一節課完全弄懂promise、async、await(三)完結篇

以前聊了異步編程的回調函數和promise,上一篇文章也說了,用promise解決異步編程,若是多個調用,就會看起來不那麼舒服。ios

es6除了提供了promise還爲咱們提供了更增強大的async和await,async、await是Generator函數的語法糖,若是想要徹底掌握async、await的用法,必需要掌握Generator函數的使用。es6

1、Generator 函數

一、什麼是 Generator 函數?

來自阮一峯老師文檔上的解釋:Generator函數是協程在 ES6 的實現,最大特色就是能夠交出函數的執行權(即暫停執行)。面試

你能夠這麼理解,這個函數本身執行不了,得讓別人幫忙執行,踢一腳(next()),走一步。編程

基本的用法:

function* doSomething() {
    yield '吃飯'
    return '睡覺'
}

let newDoSomething = doSomething() // 本身執行不了,須要指向一個狀態機

console.log(newDoSomething.next()) // {value: "吃飯", done: false}
console.log(newDoSomething.next()) // {value: "睡覺", done: true}
複製代碼
從上面的例子能夠看出來,Generator 函數有四個特色:

一、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
複製代碼
注意幾個問題:
第一個next()是用來啓動Generator函數的,傳值會被忽略,沒用
yield的用法和return比較像,你能夠當作return來用,若是yield後沒值,return undefined
最後一個next()函數,獲得的是函數return的值,若是沒有,也是undefined
完全理解了上面的概念,再來看下下面的栗子:
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
複製代碼

2、async、await

一、什麼是async、await?

async、await是Generator函數的語法糖,原理是經過Generator函數加自動執行器來實現的,這就使得async、await跟普通函數同樣了,不用再一直next執行了。

他吸取了Generator函數的優勢,能夠經過await來把函數分狀態執行,可是又不用一直next,能夠自動執行。

仍是上例子:

栗子1
function f() {
    return new Promise(resolve =>{
        resolve('hhh')
    })
}
async function doSomething1(){
    let x = await f()
}

doSomething1()

打印結果:

hhh
複製代碼

看了上面的例子,能夠看出async有三個特色:

一、函數前面會加一個async修飾符,來證實這個函數是一個異步函數;

二、await 是個運算符,用於組成表達式,它會阻塞後面的代碼

三、await 若是等到的是 Promise 對象,則獲得其 resolve值。
複製代碼
栗子2
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對象,就獲得一個表達式的運算結果。
複製代碼

async、await項目中的使用

如今有一個封裝好的,獲取數據的方法,咱們分別用promise、Generator、async來實現發請求,作一下比較:

function getList() {
    return new Promise((resolve, reject) =>{
        $axios('/pt/getList').then(res => {
            resolve(res)
        }, err => {
            reject(err)
        })
    })
}
複製代碼

promise

function initTable() {
    getList().then(res => {
        console.log(res)
    }).catch(err => {
        this.$message(err) // element的語法
    })
}
複製代碼

而後直接調用就能夠 這麼作看起來很是的簡潔,可是若是多個請求調用 就會是.then,.then看起來很是不舒服

Generator函數

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 await

async initTable() { // table列表查
  const getData = await getList(this.searchParams)
  return getData
},

getList() {
  this.initTable().then(res =>{
    this.tableList = res.data.list
  })
}
複製代碼

這樣寫好像也很簡單,並且很是方便

主要是若是調用多個接口,能夠直接多個await

總結

由於一個好朋友的一道面試題,忽然有了寫一下關於同步、異步編程的一篇文章,寫起來才發現涉及的知識點太雜,就一共寫了三篇文章:

一、關於什麼同步、異步,其中涉及了一些堆棧和消息隊列、事件輪詢的知識;

mp.weixin.qq.com/s/LAwfxg0TK…

二、關於異步編程的幾個解決方案,主要是回調函數和promise;

mp.weixin.qq.com/s/z7b5jzRd1…

三、關於異步編程的終極解決方案Generator函數以及他的語法糖async、await。

我的的微信公衆號:小Jerry有話說,平時會發一些技術文章和讀書筆記,歡迎交流。

相關文章
相關標籤/搜索