async和await對promise異步方案的改進,以及使用注意事項

async、await相比原生promise的有優點:javascript

1.更加簡潔,await一個promise便可,那麼會自動返回這個promise的resolve值,無需在then函數的回調中手動取值,完全解決了回調java

//Promise方式
 function f() {
    let promise = new Promise((resolve, reject) => {
        setTimeout(() => resolve('done!'), 1000)
    })
    promise.then((res) => {
        console.log('object :', res);
    })
}
f()
//async、await
async function f() {
    let promise = new Promise((resolve, reject) => {
        setTimeout(() => resolve('done!'), 1000)
    })
    let result = await promise // 直到promise返回一個resolve值(*)
    console.log('object :', result);
}
f()

2.避免了then鏈式調用的,沒有設置失敗回調而致使異常被拋出到then鏈的末端,而致使被忽略,向下面代碼同樣,若是then沒有設置失敗回調,那麼默認的失敗回調會將異常拋給下一個then函數的失敗回調,若是末端沒有一個catch函數。那麼異常就會丟失,問題是若是catch代碼中的異常處理代碼又有異常拋出呢,那麼這個異常只能在下一個then中捕獲,這是容易被忽略的錯誤ajax

//promise
let p = new Promise((resolve, reject) => {
    reject()
})
p.then().then().catch()

async任意一個await出現了異常,await會自動拋出reject,而且程序會被中止,異常統一在try-catch塊能夠捕獲而不會出現捕獲鏈無限延長的問題數據庫

//async、await
async function f() {
    let promise = new Promise((resolve, reject) => {
        setTimeout(() => reject('done!'), 1000)
    })
    try {
        let result = await promise // 直到promise返回一個resolve值(*)
    } catch (error) {
        console.log('object :', error);
    }
}
f()

3.then鏈式流中,數據訪問不能很天然的跨層訪問promise

MongoClient.connect(url + db_name).then(db=> {
    return db.collection('blogs');
}).then(coll=> {
    return coll.find().toArray();
}).then(blogs=> {
    console.log(blogs.length);
}).catch(err=> {
    console.log(err);

先鏈接數據庫MongoClient.connect()返回一個Promise,而後在then()方法裏得到數據庫對象db,而後再獲取到coll對象再返回。在下一個then()方法得到coll對象,而後進行查詢,查詢結果返回,逐層調用then()方法,造成一個Promise鏈。併發

這時候咱們有一個需求,第三個then(blogs => {})中咱們只能獲取到查詢的結果blogs,而不能使用上面的db對象和coll對象。這個時候,若是要打印出blogs列表後,要關閉數據庫db.close()怎麼辦?dom

兩種處理方式:異步

1.使用then嵌套async

MongoClient.connect(url + db_name).then(db=> {
    let coll = db.collection('blogs');
    coll.find().toArray().then(blogs=> {
        console.log(blogs.length);
        db.close();
    }).catch(err=> {
        console.log(err);
    });
}).catch(err=> {
    console.log(err);

問題:
這會打斷Promise鏈,致使then的回調地獄,並且致使在每個then中都須要手動捕獲異常,由於then沒成鏈,不能天然傳遞異常ide

2.每一個then()方法裏都將db傳過來

MongoClient.connect(url + db_name).then(db=> {
    return {db:db,coll:db.collection('blogs')};
}).then(result=> {
    return {db:result.db,blogs:result.coll.find().toArray()};
}).then(result=> {
    return result.blogs.then(blogs=> {   //注意這裏,result.coll.find().toArray()返回的是一個Promise,所以這裏須要再解析一層
        return {db:result.db,blogs:blogs}
    })
}).then(result=> {
    console.log(result.blogs.length);
    result.db.close();
}).catch(err=> {
    console.log(err);

問題:
咱們在then方法中,都將db和其餘結果合併成一個對象,特別須要注意的是,若是傳遞的值含有promise,那麼還須要多作一層解析,也就是須要單獨給予一個then函數進行處理,何況每次都要傳遞一個多餘的對象(對於到達實際使用地方這段路徑,這個對象是不須要使用的)

async、await方案:

let getBlogs = async function(){
    let db = await MongoClient.connect(url + db_name);
    let coll = db.collection('blogs');
    let blogs = await coll.find().toArray();
    db.close();
    return blogs;
};
 
getBlogs().then(result=> {
    console.log(result.length);
}).catch(err=> {
    console.log(err);

這裏await解決了then鏈的問題,使得then跨層訪問的問題從根本上被解決了,由於await的promise的resolve值被置於同一個做用域,能夠隨意訪問

4.使得本來異步非阻塞的表達方式,變成了更加同步阻塞的代碼,這得益於ES6中生成器和迭代器,賦予js函數的魔力,本質上,async、await是生成器和迭代器以及Promise結合的語法糖,它使得promise以前設計缺陷被更好地修正,目前看來,async、await,是異步的終極解決方案之一

async function basicDemo() {
    let result = await Math.random();
    console.log(result);
}

basicDemo();

await因爲自動返回了resolve的值,無需then,咱們甚至沒有感知到異步的存在,他將異步從語法層面上進行了同步化

async、await使用注意事項:

1.await雖然能夠像Promise.resolve做用域不少類型的數據,但它的主要意圖是用來等待Promise對象被resolved,若是await是非promise值,那麼會被當即執行

2.async函數將返回值自動包裝成一個promise,就像Promise.resolve一致的行爲

3.await必須在async函數上下文,也就是以下代碼的區別

// 正常 for 循環
async function forDemo() {
    let arr = [1, 2, 3, 4, 5];
    for (let i = 0; i < arr.length; i ++) {
        await arr[i];
    }
}
forDemo();//正常輸出
// 由於想要炫技把 for循環寫成下面這樣
async function forBugDemo() {
    let arr = [1, 2, 3, 4, 5];
    arr.forEach(item => {
        await item;
    });
}
forBugDemo();// Uncaught SyntaxError: Unexpected identifier

4.當心本身的並行處理,也許不當心就將ajax的併發請求發成了阻塞式同步的操做,理解這句話的核心是: await若等待的是promise,那麼程序就會在此處等到promise的resolved,而後繼續往下,看下面例子,這裏第一個sleep會等待自身resolved完成纔會往下,若是咱們可讓這些函數並行,同時保持await的特性,那麼效率會大大提升

function sleep(second) {
    return new Promise((resolve, reject) => {
        setTimeout(() => {
            resolve('request done! ' + Math.random());
        }, second);
    })
}

async function bugDemo() {
    await sleep(1000);
    await sleep(1000);
    await sleep(1000);
    console.log('clear the loading~');
}

bugDemo();

正確的姿式是:

async function correctDemo() {
    let p1 = sleep(1000);
    let p2 = sleep(1000);
    let p3 = sleep(1000);
    await Promise.all([p1, p2, p3]);//這裏單獨await每個promise也是同樣的效果
    console.log('clear the loading~');
}
correctDemo();// clear the loading~
相關文章
相關標籤/搜索