async await函式效能與Promise併發

zifeiyu發表於2018-12-07

有時候我們需要等待兩個非同步結果來執行接下來的操作,這個時候,如果使用async/await寫出近似於同步的流程,可能會有效能問題

我們看一下這個例子的執行時間

function foo() {
    return new Promise(function(resolve, reject) {
        setTimeout(function() {
            resolve(10)
        }, 1000)
    })
}
function bar() {
    return new Promise(function(resolve, reject) {
        setTimeout(function() {
            resolve(20)
        }, 1500)
    })
}

async function test() {
    let start = new Date().getTime()
    let val1 = await foo()
    let val2 = await bar()
    
    let val  = val1 + val2
    let end  = new Date().getTime()
    let time = end - start
    console.log(val)
    console.log(time)
    
}
// 結果
// 30  val的結果
// 2505 test()執行時間
複製程式碼

我們換一種寫法,再來看看執行時間

function foo() {
    return new Promise(function(resolve, reject) {
        setTimeout(function() {
            resolve(10)
        }, 1000)
    })
}
function bar() {
    return new Promise(function(resolve, reject) {
        setTimeout(function() {
            resolve(20)
        }, 1500)
    })
}

async function test() {
    let start = new Date().getTime()
    let p1 = foo()
    let p2 = bar()
    let val1 = await p1
    let val2 = await p2
    
    let val  = val1 + val2
    let end  = new Date().getTime()
    let time = end - start
    console.log(val)
    console.log(time)
    
}
// 結果
// 30  val的結果
// 1501 test()執行時間
複製程式碼

換一種寫法,test()的執行時間少了一秒鐘是不是?

這是因為第一種寫法,必須等foo()執行結束才能執行bar(),所以所用的時間是兩個非同步Promise等待時間的和;

而第二種寫法中,因為提前定義p1和p2,提前執行了這兩個Promise,程式執行到await p1的時候兩個Promsie都已經開始執行,也就是它們是並行的;

這樣test()的執行時間主要就取決於用時更長的那個Promise而不是兩者的相加。

或者我們也可以使用Promise.all()來實現

function foo() {
    return new Promise(function(resolve, reject) {
        setTimeout(function() {
            resolve(10)
        }, 1000)
    })
}
function bar() {
    return new Promise(function(resolve, reject) {
        setTimeout(function() {
            resolve(20)
        }, 1500)
    })
}

async function test() {
    let start = new Date().getTime()
    let vals = await Promise.all([foo(), bar()])
    
    let val  = vals[0] + vals[1]
    let end  = new Date().getTime()
    let time = end - start
    console.log(val)
    console.log(time)
    
}
// 結果
// 30  val的結果
// 1501 test()執行時間
複製程式碼

這種寫法已經相當靠譜,但是,我們還是有優化的空間;

async/await函式不應該關心非同步Promise的具體實現細節,await應該只關心最終得到的結果,這樣為更加複雜的非同步操作提供更加清晰的過程控制邏輯。

function getVal() {
    return Promise.all([foo, bar])
}

async function test() {
    let vals = await getVal()
    
    let val = vals[0] + vals[1]
    console.log(val)
}
複製程式碼

應該有意識的把這種邏輯從async/await中抽離出來,避免低層次的邏輯影響了高層次的邏輯;這也應該是所有的高度抽象化程式碼中必要的一個環節,不同邏輯層次的程式碼混雜在一起最終會像回撥地獄一樣讓自己和讀自己程式碼的人陷入混亂。

相關文章