Promise && async/await的理解和用法

kevinstory發表於2019-02-16

Promise && async/await的理解和用法

為什麼需要promise(承諾)這個東西

在之前我們處理非同步函式都是用回撥這個方法,回撥巢狀的時候會發現 閱讀性 和 除錯 的難度會增加很多;

怎麼理解promise

想象一下,你把一個任務交給一個不錯的小夥子,他叫承諾;不用擔心你交給他的任務會丟失,他總會返回的,做成功了resolve,失敗了reject;

var promise = new Promise((resolve, reject) =>{
    //交給給“承諾”同學一個非同步任務
    setTimeout(()=>{
        if(true){
            // 成功了,返回params
            resolve('params')
        }else{
            // 失敗了,返回error
            reject('error')
        }
    }, 1000) 
})

// 上面是給承諾一個任務,下面是"承諾"同學的返回
promise.then((res)=>{
    console.log(res)
}).catch((rej)=>{   
    console.log(res)
})
複製程式碼

怎麼使用promise

實際情況中,非同步的場景沒有那麼簡單,你可以會遇到下面這些場景

  1. “序列應用場景”下的處理方案
let promise = new Promise((res, rej)=>{
    asyncFunc('promise', 1000, res, rej)
}) 

promise.then(res=>{
    console.log(res);
    return new Promise((res, rej)=>{
        asyncFunc('second', 2000, res, rej)
    })
}).then(res=>{
    console.log(res);
    return new Promise((res, rej)=>{
        asyncFunc('third', 1000, res, rej)
    })
    // throw 'oh, no!';
}).then(res=>{
    console.log(res);
    console.log('endinggggggg')
}).catch(err=>{
    console.log('catch', err)
})
複製程式碼
  1. “並行應用場景”的處理方案(即在所有的非同步操作完成之後執行)
let promise1 = new Promise((res, rej)=>{
    asyncFunc('promise1', 1000, res, rej)
})

let promise2 = new Promise((res, rej)=>{
    asyncFunc('promise2', 2000, res, rej)
})

let promise3 = new Promise((res, rej)=>{
    asyncFunc('promise3', 1000, res, rej)
})

var promiseAll = Promise.all([promise1, promise2, promise3])
promiseAll.then(res =>{
    console.log('最終的結果', res)
}).catch(err =>{
    console.log('catch', err);
})
複製程式碼
  1. “競速模式下”,如字面意思,只要是哪一個提前完成了。就表示整個狀態處理完成狀態;這個場景可以發散成如果是超過了3s我就不去做這件事情了
let promise1 = new Promise((res, rej)=>{
    asyncFunc('promise1', 1000, res, rej, true)
})

let promise2 = new Promise((res, rej)=>{
    asyncFunc('promise2', 2000, res, rej, true)
})

let promise3 = new Promise((res, rej)=>{
    asyncFunc('promise3', 1000, res, rej)
})

// 1000s的任務完成了,就直接返回promise1了
var promiseRace = Promise.race([promise1, promise2, promise3])
promiseRace.then(res =>{
    console.log('最終的結果', res)
}).catch(err =>{
    console.log('catch', err);
})
複製程式碼

js是單執行緒,promise,setTimeout的執行優先順序

講這一塊的東西就得講講nodejs的事件處理機制;
事件佇列應該是一個資料結構,所有的事情都被事件迴圈排隊和處理,直到佇列為空。但是Node中的這種情況與抽象反應器模式如何描述完全不同。
下面講的東西只適合V8;

NodeJS中有許多佇列,其中不同型別的事件在自己的佇列中排隊。

在處理一個階段之後並且在移到下一個佇列之前,事件迴圈將處理兩個中間佇列,直到中間佇列中沒有剩餘的專案。

定義:

有四種主要型別,由libuv事件迴圈處理;

  • 過期的定時器和間隔佇列 - (比如使用setTimeout,setInterval);
  • IO事件佇列 - 已完成的IO事件
  • Immediates佇列 - 使用setImmediate功能新增回撥
  • 關閉處理程式佇列 - 任何close事件處理程式

還有2箇中間佇列,不屬於libuv本身的一部分,但是是nodejs的一部分;

  • Next Ticks Queue - 使用process.nextTick 函式新增回撥;(優先順序更高)
  • 其他微型任務佇列 - 包括其他微型任務,例如已經解決的承諾回撥;
如何工作的:

image

上圖是node中libuv模組在處理非同步I/O操作的流程圖;

Node通過定時器檢查佇列中的任何過期定時器來啟動事件迴圈,並在每一個步驟中遍歷每一個佇列。如果沒有任務則迴圈退出,每一次佇列處理都被視為事件迴圈的一個階段。特別有意思的是中間紅色的佇列,每次階段都會優先去處理中間佇列的任務。然後再去處理其他的佇列。

什麼是async/await

async/await 可以是Generator和promise結合實現的;

注意核心點:

  • asnyc 函式總是返回一個Promise物件,不論函式是否return Promise;
  • await 後面跟著Promise物件,如果不是Promise物件,也會被封裝成Promise;
  • async/await 和Promise物件在本質上是一樣的
其他note點
  1. await的任何內容都通過Promise.resolve()傳遞,這樣就可以安全的await非原生Promise;
  2. 建構函式以及getter/settings方法不能是非同步的;
  3. 儘管編寫的是同步的程式碼,但是也不要錯失並行執行的機會,不然你需要消耗等待的效能喪失;
  4. Babel REPL 說起來很有趣。試試就知道。

怎麼用async/await

實際情況中,非同步的場景沒有那麼簡單,你可以會遇到下面這些場景

  1. 場景:只有一個await並且 resolve
const delay = timeout => new Promise(resolve => setTimeout(resolve, timeout));
async function f(){
    await delay(1000);
    await delay(2000);
    await delay(3000);
    return 'done'
}
f().then(v=> console.log(v));
複製程式碼
  1. 場景:只有一個await並且 reject
let a;
async function g(){
    await Promise.reject('error');
    a = await 1;
}
g().then(v=>console.log(v)).catch(err=>console.log(err));
複製程式碼
  1. 場景:有多個await, 可以用try/catch
let a ;
async function g(){
    try{
        await Promise.reject('error')
    }catch(err){    
        console.log(err)
    }
    a= await 1;
    return a;
}

g().then(v=>console.log(v)).catch(err=>console.log(err));

複製程式碼
  1. 場景:等待平行任務
async function series(){
    const await1 = delay(1000);
    const await2 = delay(1000);
    await await1;
    await await2;
    return 'done'
}
series();

複製程式碼

歡迎提意見和star

如果有不對的可以提issue
如果覺得對你有幫助可以star下
github

參考文件

  1. 這一系列的文件講的很不錯 juejin.im/post/5b777f…

  2. 講promise,setTimeout優先順序的;nodejs中事件迴圈中的任務優先順序 jsblog.insiderattack.net/event-loop-…

  3. developers.google.com域名下面的文件還是很有質量的,其中會比較全面的介紹怎麼去用promise和async/await developers.google.com/web/fundame…

相關文章