【基礎覆盤】消滅異步回調,還得是 async-await
本篇,帶你讀懂 async~await 間的浪漫。
關於異步處理問題,ES5 的回調讓我們陷入回調地獄輪迴,後來 ES6 的 Promise(Promise 不瞭解?點這瞭解 [1])讓我們脫離輪迴,終於,ES7 的 async-await 帶我們走向光明。今天我們就來學習一夏 async-await,看看與 Promise 有何聯繫和區別。
一、走進 Async-await 原理
1、原理 1
async函數返回一個 Promise 對象,可以使用then方法添加回調函數
。舉例說明:
// async返回的是Promise對象?
async function testAsync() {
return 'hello';//上篇文章Promise對象的返回值如果不是Promise,會通過Promise.resolve()轉化爲Promise,再進行處理
}
const result = testAsync()
console.log(result);//Promise { 'hello' } 說明async返回的是Promise對象
複製代碼
那既然 async 返回的是 Promise 對象,那麼 async 後面的函數可以接. then() 或者. catch()... 嘛?我們試一試就知道了。
// async返回的是Promise對象,並且可以接Promise的方法?
async function testAsync() {
// await await等待還是promise對象
return 'hello'
}
testAsync()
.then((result)=>{
console.log(result);
})
.catch((error)=>{
console.log(error);
})
//hello 媽耶!打印了!說明async返回的是Promise對象,並且可以接Promise的方法,並且!!!默認狀態是resolved的
複製代碼
上面代碼說明,async函數內部return語句返回的值,會成爲then方法回調函數的參數
2、原理 2
當async函數內部拋出錯誤的時候,會導致返回的 Promise 對象變爲reject狀態
。拋出的錯誤對象會被. then() 方法的第二個回調函數接收或者. catch() 方法回調函數接收到。
// async函數內部拋出錯誤或者Promise狀態爲reject
async function testError(){
//throw new Error('出錯啦~~');
await Promise.reject('出錯了');//await前面有return和沒有return效果一樣
}
testError()
// .then(()=>{},(error)=>{console.log(error);})
.catch(error=>{console.log(error);})
//Error: 出錯啦~~
複製代碼
3、原理 3
await命令後面是一個 Promise 對象,返回該對象的結果。如果不是 Promise 對象,就直接返回對應的值
。代碼說明:
// await
async function getName(){
// return '來自星星的你';
return await '來自星星的你';//上面直接return等價於這個return
}
getName()
.then(result=>{console.log(result);})
//來自星星的你
複製代碼
4、原理 4
await的使用,必須要有async
。這便是 async-await 的浪漫所在了:async 返回的是一個 Promise 對象,await 等待的就是這個 Promise 對象,所以 await 不能沒有 async(但是 async 可以沒有 await)。有沒有被浪漫到?反正我是醉了。如果 await 沒有 async 會怎麼樣?報錯:
// await沒有async會報錯
function testAwait(){
return await '西紅柿炒辣椒'
}
testAwait()
.catch(error=>{
console.log(error);
})
//SyntaxError: await is only valid in async function
複製代碼
二、深入 Async-await 規則
1、async 封裝 Promise
// async封裝Promise
async function fn1() {
return '喜羊羊與灰太狼';// //相當於return Promise.resolve('喜羊羊與灰太狼')
const data = await fn1();//接收data值
}
fn1()//執行async函數,返回的是一個Promise對象
.then(data => {
console.log('content =', data)
})
//content = 喜羊羊與灰太狼
複製代碼
2、await 相當於 then
// await---.then()
async function getName(){
const operate=Promise.resolve('白雪公主')//執行函數
const name= await operate //await相當於Promise的then operate.then(name=>{})
console.log('name:',name)
}
getName();
( async function(){
const person=await '七個小矮人' //await Promise.resolve('七個小矮人') await後面不跟Promise,也會被封裝成Promise
console.log('person:',person)//400
})();//自執行函數
//name: 白雪公主
//person: 七個小矮人
複製代碼
3、多個 await 時,按時序執行
當函數執行的時候,一旦遇到 await 就會先返回,等到異步操作完成,再接着執行函數體內後面的語句。任何一個await語句後面的 Promise 對象變爲reject狀態,那麼整個async函數都會中斷執行
。
async function testOrder() {
await Promise.reject('出錯了')//UnhandledPromiseRejectionWarning: 出錯了
await Promise.resolve('hello world'); // 不會執行
}
testOrder();
複製代碼
4、try…catch 相當於 catch
如果希望即使前一個異步操作失敗,也不要中斷後面的異步操作。可將第一個await放在try...catch結構裏面
,這樣不管這個異步操作是否成功,第二個 await 都會執行。
// try...catch
!(async function () {
const testError = Promise.reject('出錯啦~~~')//rejected狀態
// const testError=throw new Error('出錯啦~~~');
try {
const result = await testError; //await相當於then,但是reject不會觸發then
console.log('success:'+result) //不會輸出,因爲const result = await testError被報錯,被catch捕獲
} catch (error) {
console.error('error:'+error)//try...catch 相當於Promise的catch
}
})()
//error:出錯啦~~~
複製代碼
當 await 後面是 Promise 對象的時候,我們也可直接在await後面直接.catch捕獲
錯誤:
async function testError() {
await Promise.reject('出錯了')
.catch(error => console.log(error));//這裏捕獲錯誤,不會影響下一個await執行
return await Promise.resolve('hello world');
}
testError()
.then(result => console.log(result))
複製代碼
三、解析 Async-await 語法
我們淺淺看一個面試題:
// 面試題
function getJSON() {
return new Promise((resolve, reject) => {
setTimeout(() => {
console.log(2);
resolve(2)
}, 2000)
})
}
async function testAsync() {
await getJSON()
console.log(3);
}
testAsync()
//2
//3
複製代碼
問題當然不會問打印順序啦,問題是將 async await 語句解析翻譯爲 Promise?
根據現在的知識面,我們必須知道:
(1)await 不能單獨出現,其函數前面一定要有 async。
(2)await 會幹兩件事:
第一,將寫在 await 後面的代碼放到 async 創建的那個 Promise 裏面執行。
第二、將寫在 await 下面的代碼放到前一個創建的那個 Promise 對象的. then 裏面執行。
(3)await 返回的也是 Promise 對象,他只是把 await 下面的代碼放到了 await 返回的 promise 的. then 裏面執行。
這樣的話,是不是如魚得水了。翻譯如下:
function getJSON() {
return new Promise((resolve, reject) => {
setTimeout(() => {
console.log(2);
resolve(2)
}, 2000)
})
}
// 編譯成Promise原理
function testAsync() {
return Promise.resolve().then(() => {
return getJSON();
})
.then(() => {
console.log(3);
})
}
testAsync()
複製代碼
四、拓展 Async-await 應用
1、場景 1
你學廢 async-await 了嘛?還記得上一篇開篇的回調地獄嘛?我們通過 Promise 解決回調是這樣的:
// Promise解決方式
function doCallback(n) {
var myPromise = new Promise(function (resolve, reject) {
//處理異步任務
var flag = true;
setTimeout(function () {
if (flag) {
resolve(n)
}
else {
reject('失敗')
}
},0)
})
return myPromise;
}
doCallback(1)
.then((result) => { //then是成功執行的方法 返回的還是一個Promise對象
console.log(result);//打印張三 res是執行
return fn(2);
})
.then((result) => {
console.log(result);
return fn(3)
})
.then((result) => {
console.log(result);
return fn(4)
})
.then((result) => {
console.log(result);
})
.catch((result) => { //catch是失敗執行的方法
console.log(result);
})
//好多.then,形成.then鏈啦
//1
//2
//3
//4
複製代碼
通過以上 Promise 方法,可以明顯解決回調地獄 “向右移” 的浮誇表現,但是,Promise是基於 then, catch 的鏈式調用,但也是基於回調函數
。.then 鏈多多少少還是違背原生代碼,顯得也不是很優雅。作爲回調終極武器,async-await 更加貼近於原生代碼,我們看一下吧:
//封裝一個返回promise的異步任務
function doCallback(str) {
var myPromise = new Promise(function (resolve, reject) {
var flag = true;
setTimeout(function () {
if (flag) {
resolve(str)
} else {
reject('處理失敗')
}
})
})
return myPromise;
}
//封裝一個執行上述異步任務的async函數
async function testAsync() {
var result1 = await doCallback(1); //await直接拿到fn()返回的promise的數據,並且賦值給result
var result2 = await doCallback(2); //await 後面的代碼,都可以看做是異步回調 callback 裏的內容,都是異步的
var result3 = await doCallback(3);
var result4 = await doCallback(4);
console.log(result1);
console.log(result2);
console.log(result3);
console.log(result4);
}//這樣是不是簡潔優雅多了呢?
//執行函數
testAsync();
//1
//2
//3
//4
複製代碼
有了 async-await、promise 還有必要學習嗎?通過上面 async-await 的解決方案可以瞧見,async / await 和 Promise 並不互斥,二者相輔相成
。同時 async / await 並不能改變異步的本質( js 是單線程的,異步需要回調,都是要基於 event loop 來實現 (什麼是 event loop? 關注我,等我文章~))。
總結
現在知道了,async-await 是 promise 的語法糖了吧,不僅讓我們書寫代碼時更加流暢,而且增強了代碼的可讀性。特別注意的是:雖然 async-await 是建立在 Promise 機制之上的,但是並不能取代其地位,他們兩者相輔相成,息息相關。其實 async-await 不止是 Promise 的語法糖,還是 Generator 的語法糖,Generator 是什麼?我們下篇見分曉。
關於本文
作者:來碗鹽焗星球
https://juejin.cn/post/7122478595787718663
本文由 Readfog 進行 AMP 轉碼,版權歸原作者所有。
來源:https://mp.weixin.qq.com/s/N-XXXdX0visfKKunOcPYbw