async-await消滅異步回調(diào)實(shí)例詳解
引言
本篇,帶你讀懂a(chǎn)sync~await間的浪漫。
關(guān)于異步處理問題,ES5的回調(diào)讓我們陷入回調(diào)地獄輪回,后來ES6的Promise(Promise不了解?點(diǎn)這了解)讓我們脫離輪回,終于,ES7的async-await帶我們走向光明。今天我們就來學(xué)習(xí)一夏 async-await,看看與Promise有何聯(lián)系和區(qū)別。
一、走進(jìn)Async-await原理
1、原理1
async函數(shù)返回一個(gè) Promise 對(duì)象,可以使用then方法添加回調(diào)函數(shù)。舉例說明:
// async返回的是Promise對(duì)象?
async function testAsync() {
return 'hello';//上篇文章Promise對(duì)象的返回值如果不是Promise,會(huì)通過Promise.resolve()轉(zhuǎn)化為Promise,再進(jìn)行處理
}
const result = testAsync()
console.log(result);//Promise { 'hello' } 說明async返回的是Promise對(duì)象
那既然async返回的是Promise對(duì)象,那么async后面的函數(shù)可以接.then()或者.catch()...嘛?我們?cè)囈辉嚲椭懒恕?/p>
// async返回的是Promise對(duì)象,并且可以接Promise的方法?
async function testAsync() {
// await await等待還是promise對(duì)象
return 'hello'
}
testAsync()
.then((result)=>{
console.log(result);
})
.catch((error)=>{
console.log(error);
})
//hello 媽耶!打印了!說明async返回的是Promise對(duì)象,并且可以接Promise的方法,并且?。?!默認(rèn)狀態(tài)是resolved的
上面代碼說明,async函數(shù)內(nèi)部return語句返回的值,會(huì)成為then方法回調(diào)函數(shù)的參數(shù)
2、原理2
當(dāng)async函數(shù)內(nèi)部拋出錯(cuò)誤的時(shí)候,會(huì)導(dǎo)致返回的 Promise 對(duì)象變?yōu)閞eject狀態(tài)。拋出的錯(cuò)誤對(duì)象會(huì)被.then()方法的第二個(gè)回調(diào)函數(shù)接收或者.catch()方法回調(diào)函數(shù)接收到。
// async函數(shù)內(nèi)部拋出錯(cuò)誤或者Promise狀態(tài)為reject
async function testError(){
//throw new Error('出錯(cuò)啦~~');
await Promise.reject('出錯(cuò)了');//await前面有return和沒有return效果一樣
}
testError()
// .then(()=>{},(error)=>{console.log(error);})
.catch(error=>{console.log(error);})
//Error: 出錯(cuò)啦~~
3、原理3
await命令后面是一個(gè) Promise 對(duì)象,返回該對(duì)象的結(jié)果。如果不是 Promise 對(duì)象,就直接返回對(duì)應(yīng)的值。代碼說明:
// await
async function getName(){
// return '來自星星的你';
return await '來自星星的你';//上面直接return等價(jià)于這個(gè)return
}
getName()
.then(result=>{console.log(result);})
//來自星星的你
4、原理4
await的使用,必須要有async。這便是async-await的浪漫所在了:async返回的是一個(gè)Promise對(duì)象,await等待的就是這個(gè)Promise對(duì)象,所以await不能沒有async(但是async可以沒有await)。有沒有被浪漫到?反正我是醉了。如果await沒有async會(huì)怎么樣?報(bào)錯(cuò):
// await沒有async會(huì)報(bào)錯(cuò)
function testAwait(){
return await '西紅柿炒辣椒'
}
testAwait()
.catch(error=>{
console.log(error);
})
//SyntaxError: await is only valid in async function
二、深入Async-await規(guī)則
1、async封裝Promise
// async封裝Promise
async function fn1() {
return '喜羊羊與灰太狼';// //相當(dāng)于return Promise.resolve('喜羊羊與灰太狼')
const data = await fn1();//接收data值
}
fn1()//執(zhí)行async函數(shù),返回的是一個(gè)Promise對(duì)象
.then(data => {
console.log('content =', data)
})
?
//content = 喜羊羊與灰太狼
2、await相當(dāng)于then
// await---.then()
async function getName(){
const operate=Promise.resolve('白雪公主')//執(zhí)行函數(shù)
const name= await operate //await相當(dāng)于Promise的then operate.then(name=>{})
console.log('name:',name)
}
getName();
( async function(){
const person=await '七個(gè)小矮人' //await Promise.resolve('七個(gè)小矮人') await后面不跟Promise,也會(huì)被封裝成Promise
console.log('person:',person)//400
})();//自執(zhí)行函數(shù)
?
//name: 白雪公主
//person: 七個(gè)小矮人
3、多個(gè)await時(shí),按時(shí)序執(zhí)行
當(dāng)函數(shù)執(zhí)行的時(shí)候,一旦遇到await就會(huì)先返回,等到異步操作完成,再接著執(zhí)行函數(shù)體內(nèi)后面的語句。任何一個(gè)await語句后面的 Promise 對(duì)象變?yōu)閞eject狀態(tài),那么整個(gè)async函數(shù)都會(huì)中斷執(zhí)行。
async function testOrder() {
await Promise.reject('出錯(cuò)了')//UnhandledPromiseRejectionWarning: 出錯(cuò)了
await Promise.resolve('hello world'); // 不會(huì)執(zhí)行
}
testOrder();
4、try…catch相當(dāng)于catch
如果希望即使前一個(gè)異步操作失敗,也不要中斷后面的異步操作??蓪⒌谝粋€(gè)await放在try...catch結(jié)構(gòu)里面,這樣不管這個(gè)異步操作是否成功,第二個(gè)await都會(huì)執(zhí)行。
// try...catch
!(async function () {
const testError = Promise.reject('出錯(cuò)啦~~~')//rejected狀態(tài)
// const testError=throw new Error('出錯(cuò)啦~~~');
try {
const result = await testError; //await相當(dāng)于then,但是reject不會(huì)觸發(fā)then
console.log('success:'+result) //不會(huì)輸出,因?yàn)閏onst result = await testError被報(bào)錯(cuò),被catch捕獲
} catch (error) {
console.error('error:'+error)//try...catch 相當(dāng)于Promise的catch
}
?
})()
//error:出錯(cuò)啦~~~
當(dāng)await后面是Promise對(duì)象的時(shí)候,我們也可直接在await后面直接.catch捕獲錯(cuò)誤:
async function testError() {
await Promise.reject('出錯(cuò)了')
.catch(error => console.log(error));//這里捕獲錯(cuò)誤,不會(huì)影響下一個(gè)await執(zhí)行
return await Promise.resolve('hello world');
}
?
testError()
.then(result => console.log(result))
三、解析Async-await語法
我們淺淺看一個(gè)面試題:
// 面試題
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
問題當(dāng)然不會(huì)問打印順序啦,問題是將async await語句解析翻譯為Promise?
根據(jù)現(xiàn)在的知識(shí)面,我們必須知道:
(1)await不能單獨(dú)出現(xiàn),其函數(shù)前面一定要有async。
(2)await會(huì)干兩件事:
第一,將寫在await后面的代碼放到async創(chuàng)建的那個(gè)Promise里面執(zhí)行。
第二、將寫在await下面的代碼放到前一個(gè)創(chuàng)建的那個(gè)Promise對(duì)象的.then里面執(zhí)行。
(3)await返回的也是Promise對(duì)象,他只是把a(bǔ)wait下面的代碼放到了await返回的promise的.then里面執(zhí)行。
這樣的話,是不是如魚得水了。翻譯如下:
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應(yīng)用
1、場(chǎng)景1
你學(xué)廢async-await了嘛?還記得上一篇開篇的回調(diào)地獄嘛?我們通過Promise解決回調(diào)是這樣的:
// Promise解決方式
function doCallback(n) {
var myPromise = new Promise(function (resolve, reject) {
//處理異步任務(wù)
var flag = true;
setTimeout(function () {
if (flag) {
resolve(n)
}
else {
reject('失敗')
}
},0)
})
return myPromise;
}
?
doCallback(1)
.then((result) => { //then是成功執(zhí)行的方法 返回的還是一個(gè)Promise對(duì)象
console.log(result);//打印張三 res是執(zhí)行
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是失敗執(zhí)行的方法
console.log(result);
})
//好多.then,形成.then鏈啦
//1
//2
//3
//4
通過以上Promise方法,可以明顯解決回調(diào)地獄“向右移”的浮夸表現(xiàn),但是,Promise是基于 then, catch 的鏈?zhǔn)秸{(diào)用,但也是基于回調(diào)函數(shù)。.then鏈多多少少還是違背原生代碼,顯得也不是很優(yōu)雅。作為回調(diào)終極武器,async-await更加貼近于原生代碼,我們看一下吧:
//封裝一個(gè)返回promise的異步任務(wù)
function doCallback(str) {
var myPromise = new Promise(function (resolve, reject) {
var flag = true;
setTimeout(function () {
if (flag) {
resolve(str)
} else {
reject('處理失敗')
}
})
})
return myPromise;
}
?
//封裝一個(gè)執(zhí)行上述異步任務(wù)的async函數(shù)
async function testAsync() {
var result1 = await doCallback(1); //await直接拿到fn()返回的promise的數(shù)據(jù),并且賦值給result
var result2 = await doCallback(2); //await 后面的代碼,都可以看做是異步回調(diào) callback 里的內(nèi)容,都是異步的
var result3 = await doCallback(3);
var result4 = await doCallback(4);
console.log(result1);
console.log(result2);
console.log(result3);
console.log(result4);
}//這樣是不是簡(jiǎn)潔優(yōu)雅多了呢?
//執(zhí)行函數(shù)
testAsync();
//1
//2
//3
//4
有了 async-await、promise 還有必要學(xué)習(xí)嗎?通過上面async-await的解決方案可以瞧見,async / await 和 Promise 并不互斥,二者相輔相成。同時(shí)async / await 并不能改變異步的本質(zhì)( js是單線程的,異步需要回調(diào),都是要基于 event loop 來實(shí)現(xiàn)(什么是event loop?關(guān)注我,等我文章~))。
總結(jié)
現(xiàn)在知道了,async-await是promise的語法糖了吧,不僅讓我們書寫代碼時(shí)更加流暢,而且增強(qiáng)了代碼的可讀性。特別注意的是:雖然async-await 是建立在 Promise機(jī)制之上的,但是并不能取代其地位,他們兩者相輔相成,息息相關(guān)。
其實(shí)async-await不止是Promise的語法糖,還是Generator的語法糖,Generator是什么?更多關(guān)于async-await消滅異步回調(diào)的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
Meta開源JavaScript內(nèi)存泄漏監(jiān)測(cè)工具M(jìn)emLab安裝使用
這篇文章主要為大家介紹了Meta開源JavaScript內(nèi)存泄漏監(jiān)測(cè)工具M(jìn)emLab安裝使用詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2022-09-09
詳解微信小程序開發(fā)之——wx.showToast(OBJECT)的使用
本篇文章主要介紹了微信小程序開發(fā)之——wx.showToast(OBJECT)的使用,具有一定的參考價(jià)值,有興趣的可以了解一下。2017-01-01
LoadRunner調(diào)用JS加密后登錄實(shí)現(xiàn)
這篇文章主要為大家介紹了LoadRunner調(diào)用JS加密后登錄實(shí)現(xiàn)示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-06-06

