異步編程(2):async/await

異步編程

上一篇文章講了 PromisePromise 讓咱們更友好的處理異步請求,尤爲是在多個回調相互依賴等待的時候。可是它不是異步的最佳解決方案,因此從ES2017開始官方引入了 async 方法。下面咱們來看一下。javascript

定義

先來看一下MDN上對他的定義。java

聲明一個異步函數,返回的是一個Promise對象。並且在 async 函數中可使用 await 表達式。編程

async 是什麼

不得不說標準委員會的每一個定義都是有其考慮的,名字通常都言簡意賅,直明其意。從字面意思咱們能知道它是異步的意思。promise

那他究竟是怎麼異步的呢。咱們先看下他的返回值。異步

官方定義async 返回一個 Promise 對象。若是在函數中直接return 的是Promise 則直接返回。若是 return 出來的不是Promise 則對其包裝成Promise。async

async function a1(){
  return 'hello world'
}

let result = a1()

複製代碼

上面的demo 返回值 result 就是一個Promise 對象。他和其餘經過 Promise 顯示聲明的對象是同樣的,也具有三種狀態,也有 then 等方法,也能夠鏈式調用。異步編程

async 正是經過其返回Promise對象保證異步操做。函數

await 等待

標準中還說了,async中可使用 await ,後面跟一個表達式。這個表達式能夠是一個Promise對象或者任何其餘的表達式。post

那這個await的做用是什麼呢?ui

await 會暫停當前 async 方法的執行,等待後面的Promise處理完成。這裏咱們要注意,await 暫停的是async 方法內的語句執行,可是不會阻塞async 方法以後的內容。

async function a1(){
    console.log('a1 start')
    await new Promise((resolve,reject) => {
        setTimeout( () => {
            resolve()
            console.log('resolve')
        },1000)
    })
    console.log('a1 end ')
}

function a2(){
    console.log('this is from a2')
}

a1();
a2();

// 正確輸出爲: 
// a1 start
// this is from a2
// resolve
// a1 end
複製代碼

咱們來看一下上面的demo。首先 await 確實是暫停了執行,等待 promise有告終果在執行的後面的代碼,其次是,a1方法內部awati暫停的時候,不會阻塞後面a2 方法的執行。

這是爲何呢?咱們都知道JS是單線程語言,異步是它的靈魂,即便 async 方法也不會改變這個事實,它依然是單線程,依然不會阻塞。

可是在 async 函數的內部,咱們使用await 看起來就跟同步執行是同樣的,其實這只是一個障眼法,他只是一個語法糖。

上面await 後面的表達式是一個Promise對象,那麼若是是普通的表達式呢?

咱們來看下:

async function a1(){
    console.log('a1 start')
    await a3();
    console.log('a1 end ')
}

function a2(){
    console.log('this is from a2')
}

function a3(){
    console.log('a3')
}
a1();
a2();

// a1 start
// a3
// this is from a2
// a1 end
複製代碼

此次 await 後面接了一個普通的函數,咱們看到await 後面的方法當即執行了,可是a1 方法仍是暫停執行。也就是說不管後面跟什麼表達式,await都會暫停執行async 方法。

使用姿式

咱們差很少大概瞭解了async,具體的細節你能夠去看官方文檔或者其餘文章,已經有不少了就不羅列了。咱們如今看一下他的使用場景。

假設有這麼一種場景 c依賴b,b依賴a。咱們來比較下promise和 async的使用區別。

// Promise

function a(res){
    return new Promise((resolve,reject) => {
        setTimeout( () => {
            resolve(res + 100)
        },1000)
    })
}

function b(res){
    return new Promise((resolve,reject) => {
        setTimeout( () => {
            resolve(res+ 100)
        },1000)
    })
}

function c(res){
    return new Promise((resolve,reject) => {
        setTimeout( () => {
            resolve(res + 100)
        },1000)
    })
}

a(300).then(res => {
        return b(res)
    })
    .then(res => {
        return c(res)
    })
	.then(res => {
      console.log(res)
    })

// 600
複製代碼

下面咱們換成async 方法。

// async

function a(res){
    return new Promise((resolve,reject) => {
        setTimeout( () => {
            resolve(res + 100)
        },1000)
    })
}

function b(res){
    return new Promise((resolve,reject) => {
        setTimeout( () => {
            resolve(res+ 100)
        },1000)
    })
}

function c(res){
    return new Promise((resolve,reject) => {
        setTimeout( () => {
            resolve(res + 100)
        },1000)
    })
}


async function getResult(){
    var res = 300;
    res = await a(res);
    res = await b(res);
    res = await c(res);
    console.log(res);
}

getResult()

複製代碼

是否是看着更直觀,更清晰了些。

另外還有個關鍵點要注意:

await 後面若是是異步的話,必定要是promise對象。

有兩種方式,一種是上面那樣本身聲明,一種是跟一個 async 方法。

試想一種情景,await後面跟一個異步請求函數。若是這個函數最後返回的不是promise對象,那麼他就是普通表達式。上面講了,他會當即執行,await 表達式是拿不到你想要的異步請求的結果的。

容易誤區

有一些常見的用法咱們要注意下,如下爲僞代碼

async function(){
  let a = await getDate1();
  let b = await getDate2();
}
複製代碼

這一段代碼,語法是沒問題。可是這個通常狀況是b對a有依賴關係,若是沒有關係應該這樣寫。

async function(){
  let a = getDate1();
  let b = getDate2();
  await a;
  await b;
}

// 這樣a,b不存在等待關係
複製代碼

一樣道理,假設如下情景,b依賴a, d依賴c。你可能會這樣寫

// 僞代碼

let res1 = a();
let res2 = c();

await res1;
b();
await res2;
d();
複製代碼

這有個啥問題呢,就是d等待的實際上是a和c耗時最長的那個。咱們能夠換一種方式,把他倆放在兩個函數裏面。

// 僞代碼
async function ab(){
  await a();
  b()
}

async function cd(){
  await c();
  d()
}

Promise.all([ab(),cd()])
複製代碼

錯誤處理

有時候咱們可能會遇到await 等待的promise返回的是一個reject狀態。那咱們應該怎麼處理。

function a(){
    return new Promise((resolve,reject) => {
        reject('this is error')
    })
}

//第一種 trycatch 捕獲
async function getResult(){
    try {
        await a()
    } catch (error) {
        console.log(error)
    }
}

getResult()

//第二種 經過catch 捕獲
async function getResult(){
    await a().catch(error =>{
        console.log(error);
    })
}

getResult()

// 第三種 經過catch捕獲
async function getResult(){
    await a();
}

getResult().catch(error => {
    console.log(error)
})

複製代碼

好了,差很少async 大部分知識點都理清了。主要是幾個關鍵點要注意到。但願以上對你們有一點小小的幫助,若是有不足和錯誤之處,望你們多提意見。

端午假期立刻就過去了,明天又能夠開心的搬磚了。

參考連接:

developer.mozilla.org/zh-CN/docs/…

相關文章
相關標籤/搜索