使用async/await更好的解決非同步問題

天邊的一片雲發表於2019-03-04

一、如何使用async/await

async函式是Generator的一個語法糖,使用async函式實際上返回的是一個Promise物件。如下:

async function fn(){
    return 30;
}

// 或者
const fn = async () =>{
    return 30;
}複製程式碼

在宣告函式的時候,前面加上關鍵字async即可。我們可以使用 console.log列印出上邊宣告的函式fn,結果如下:

console.log(fn());
// result
Promise = {
    _proto_: Promise,
    [[PromiseStatus]]: "resolved",
    [[PromiseValue]]: 30
}複製程式碼

顯然,fn的執行結果其實就是一個Promise物件,因此我們可以使用then來處理後續邏輯。

fn().then(res =>{
    console.log(res);
})複製程式碼

await的是含義是等待。意思是程式碼需要等待await後面的函式執行完成並且有了返回結果之後,才繼續執行後續的程式碼,以此來實現同步的效果。 需要注意的是,await關鍵字只能在async函式中使用,並且await後面的函式執行後必須返回一個Promise物件才能實現同步的效果

當我們使用一個變數去接收await的返回值時,該返回值是Promise中resolve出來的值(也就是PromiseValue)。

// 定一個返回Promise物件的函式
function f1(){
    return new Promise((resolve,reject) =>{
        setTimeout(() =>{
            resolve(1);
        },1000);
    })
}
function f2(){
    return new Promise((resolve,reject) =>{
        setTimeout(() =>{
            resolve(2);
        },1000);
    })
}
function f3(){
    return new Promise((resolve,reject) =>{
        setTimeout(() =>{
            resolve(3);
        },1000);
    })
}

const foo = async () =>{
    const result_1 = await f1();
    console.log(result_1);
    const result_2 = await f2();
    console.log(result_2);
    const c = await f3();
    console.log(result_3);
}
foo();

// 執行結果:
// 1
// 2
// 3複製程式碼

執行這個例子我們可以看出,當在async函式中,執行遇到await時,就會等待await後面的函式執行完畢,而不會直接執行後續程式

二、async/await相對於直接使用Promise的優勢

如果我們直接使用Promise的話,想要實現以上的結果,就不得不把後續的邏輯寫在then方法中。

function f1(){
    return new Promise((resolve,reject) =>{
        setTimeout(() =>{
            resolve(1);
        },1000);
    })
}
function f2(){
    return new Promise((resolve,reject) =>{
        setTimeout(() =>{
             resolve(2);
        },1000);
    })
}
function f3(){
    return new Promise((resolve,reject) =>{
        setTimeout(() =>{
            resolve(3);
        },1000);
    })
}

const foo = () =>{
    return f1().then(t =>{
        console.log(t);
        return f2();
    }).then(t =>{
        console.log(t);
        return f3();
    }).then(t =>{
        console.log(t)
    });
}
foo();複製程式碼

異常處理

在Promise中,我們知道是通過catch的方式來捕獲異常。而當我們使用async時,則是通過try/catch來捕獲異常。

function fn(){
    return new Promise((resolve,reject) =>{
        setTimeout(() =>{
            reject("some error.");
        },1000)
    })
}

const foo = asyn () =>{
    try{
        await fn();
    }catch(e){
        console.log(e); //some error.
    }
}

foo();複製程式碼

如果有多個await函式,那麼只會返回第一個捕獲到的異常。

function f1(){
    return new Promise((resolve,reject) =>{
        setTimeout(() =>{
            reject("f1 error");
        },1000)
    })
}

function f2(){
    return new Promise((resolve,reject) =>{
        setTimeout(() =>{
            reject("f2 error");
        },1000)
    })
}

function f3(){
    return new Promise((resolve,reject) =>{
        setTimeout(() =>{
            reject("f3 error");
        },1000)
    })
}


const foo = async () =>{
    try{
        await f1();
        await f2();
        await f3();
    }catch(e){
        console.log(e); //f1 error
    }
}

foo();複製程式碼

如果我們直接使用Promise,實現以上效果,程式碼應該寫成如下:

function f1(){
    return new Promise((resolve,reject) =>{
        setTimeout(() =>{
            reject("f1 error");
        },1000)
    })
}

function f2(){
    return new Promise((resolve,reject) =>{
        setTimeout(() =>{
            reject("f2 error");
        },1000)
    })
}

function f3(){
    return new Promise((resolve,reject) =>{
        setTimeout(() =>{
            reject("f3 error");
        },1000)
    })
}

const foo = () =>{
    return f1().then(() =>{
        return f2();
    }).then(() =>{
        return f3();
    }).catch(err =>{
        console.log(err);
    })
}

foo();複製程式碼

很顯然,如果使用async/await的話,程式碼結構會更加簡潔,邏輯也更加清晰,更利於專案中的開發以及維護。


相關文章