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~