解决异步问题,教你如何写出优雅的promise和async/await,告别callback回调地狱!
解決異步問(wèn)題——promise、async/await
- 一、單線(xiàn)程和異步
- 1、單線(xiàn)程是什么
- 2、為什么需要異步
- 3、使用異步的場(chǎng)景
- 二、promise
- 1、promise的三種狀態(tài)
- 2、三種狀態(tài)的表現(xiàn)和變化
- (1)狀態(tài)的變化
- (2)狀態(tài)的表現(xiàn)
- 3、then和catch對(duì)狀態(tài)的影響(重要)
- 4、then和catch的鏈?zhǔn)秸{(diào)用(常考)
- 三、async/await
- 1、引例闡述
- 2、async和await
- 3、async/await和promise的關(guān)系
- 4、異步的本質(zhì)
- 5、場(chǎng)景題
- (1)async/await語(yǔ)法
- (2)async/await的順序問(wèn)題
- 四、寫(xiě)在最后
我們平常在寫(xiě) js 中,經(jīng)常只管程序能跑就行,但很少去深究它的原理。更別說(shuō)還有一個(gè)同步和異步的問(wèn)題了。因此,程序往往有時(shí)候出現(xiàn)莫名其妙的卡死或者有時(shí)候執(zhí)行順序達(dá)不到我們想要的結(jié)果時(shí)自己都不知道往哪里找錯(cuò)。下面的這篇文章中,將講解同步和異步的問(wèn)題,以及如何解決異步問(wèn)題的promise、async/await方法。
在看本文之前,建議大家先對(duì) event loop 有一個(gè)了解。可以先看我的上一篇文章了解event loop以及微任務(wù)宏任務(wù)的相關(guān)知識(shí),這樣到后面看本文的 async/await 的時(shí)候會(huì)更友好一些。
下面開(kāi)始進(jìn)行本文的講解。
一、單線(xiàn)程和異步
1、單線(xiàn)程是什么
(1) JS 是單線(xiàn)程語(yǔ)言,只能同時(shí)做一件事情
- 所謂單線(xiàn)程,就是只能同時(shí)做一件事情,多一件都不行,這就是單線(xiàn)程。
(2) 瀏覽器和 nodejs 已支持 JS 啟動(dòng)進(jìn)程,如 Web Worker
(3) JS 和 DOM 渲染共用同一個(gè)線(xiàn)程,因?yàn)?JS 可修改 DOM 結(jié)構(gòu)
- JS 可以修改 DOM 結(jié)構(gòu),使得它們必須共用同一個(gè)線(xiàn)程,這間接算是一件迫不得已的事情。
- 所以當(dāng) DOM 在渲染時(shí) JS 必須停止執(zhí)行,而 JS 執(zhí)行過(guò)程 DOM 渲染也必須停止。
2、為什么需要異步
當(dāng)程序遇到網(wǎng)絡(luò)請(qǐng)求或定時(shí)任務(wù)等問(wèn)題時(shí),這個(gè)時(shí)候會(huì)有一個(gè)等待時(shí)間。
假設(shè)一個(gè)定時(shí)器設(shè)置10s,如果放在同步任務(wù)里,同步任務(wù)會(huì)阻塞代碼執(zhí)行,我們會(huì)等待10s后才能看到我們想要的結(jié)果。1個(gè)定時(shí)器的等待時(shí)間可能還好,如果這個(gè)時(shí)候是100個(gè)定時(shí)器呢?我們總不能等待著1000s的時(shí)間就為了看到我們想要的結(jié)果吧,這幾乎不太現(xiàn)實(shí)。
那么這個(gè)時(shí)候就需要異步,通過(guò)異步來(lái)讓程序不阻塞代碼執(zhí)行,靈活執(zhí)行程序。
3、使用異步的場(chǎng)景
(1)異步的請(qǐng)求,如ajax圖片加載
//ajax console.log('start'); $.get('./data1.json', function (data1) {console.log(data1); }); console.log('end');(2)定時(shí)任務(wù),如setTimeout、setInterval
//setTimeout console.log(100); setTimeout(fucntion(){console.log(200); }, 1000); console.log(300); //setInterval console.log(100); setInterval(fucntion(){console.log(200); }, 1000); console.log(300);二、promise
早期我們?cè)诮鉀Q異步問(wèn)題的時(shí)候,基本上都是使用callback回調(diào)函數(shù)的形式 來(lái)調(diào)用的。形式如下:
//獲取第一份數(shù)據(jù) $.get(url1, (data1) => {console.log(data1);//獲取第二份數(shù)據(jù)$.get(url2, (data2) => {console.log(data2);//獲取第三份數(shù)據(jù)$.get(url3, (data3) => {console.log(data3);//還可以獲取更多數(shù)據(jù)});}); });從上述代碼中可以看到,早期在調(diào)用數(shù)據(jù)的時(shí)候,都是一層套一層, callback 調(diào)用 callback ,仿佛深陷調(diào)用地獄一樣,數(shù)據(jù)也被調(diào)用的非常亂七八糟的。所以,因?yàn)?callback 對(duì)開(kāi)發(fā)如此不友好,也就有了后來(lái)的 promise 產(chǎn)生, promise 的出現(xiàn)解決了 callback hell 的問(wèn)題。
用一段代碼先來(lái)了解一下 Promise 。
function getData(url){return new Promise((resolve, reject) => {$.ajax({url,success(data){resolve(data);},error(err){reject(err);}});}); }const url1 = '/data1.json'; const url2 = '/data2.json'; const url3 = './data3.json'; getData(url1).then(data1 => {console.log(data1);return getData(url2); }).then(data2 => {console.log(data2);return getData(url3); }).then(data3 => {console.log(data3); }).catch(err => console.error(err));大家可以看到,運(yùn)用了 promise 之后,代碼不再是一層套一層,而是通過(guò) .then 的方式來(lái)對(duì)數(shù)據(jù)進(jìn)行一個(gè)獲取,這在寫(xiě)法上就已經(jīng)美觀了不少。那 promise 究竟是什么呢?接下來(lái)開(kāi)始進(jìn)行講解。
1、promise的三種狀態(tài)
- pending :等待狀態(tài),即在過(guò)程中,還沒(méi)有結(jié)果。比如正在網(wǎng)絡(luò)請(qǐng)求,或定時(shí)器沒(méi)有到時(shí)間。
- fulfilled :滿(mǎn)足狀態(tài),即事件已經(jīng)解決了,并且成功了;當(dāng)我們主動(dòng)回調(diào)了 fulfilled 時(shí),就處于該狀態(tài),并且會(huì)回調(diào) then 函數(shù)。
- rejected :拒絕狀態(tài),即事件已經(jīng)被拒絕了,也就是失敗了;當(dāng)我們主動(dòng)回調(diào)了 reject 時(shí),就處于該狀態(tài),并且會(huì)回調(diào) catch 函數(shù)。
總結(jié):
- 只會(huì)出現(xiàn)pending → fulfilled,或者pending → rejected 狀態(tài),即要么成功要么失敗;
- 不管是成功的狀態(tài)還是失敗的狀態(tài),結(jié)果都不可逆。
2、三種狀態(tài)的表現(xiàn)和變化
(1)狀態(tài)的變化
promise 主要有以上三種狀態(tài), pending 、 fulfilled 和 rejected 。當(dāng)返回一個(gè) pending 狀態(tài)的 promise 時(shí),不會(huì)觸發(fā) then 和 catch 。當(dāng)返回一個(gè) fulfilled 狀態(tài)時(shí),會(huì)觸發(fā) then 回調(diào)函數(shù)。當(dāng)返回一個(gè) rejected 狀態(tài)時(shí),會(huì)觸發(fā) catch 回調(diào)函數(shù)。那在這幾個(gè)狀態(tài)之間,他們是怎么變化的呢?
1)演示1
先來(lái)看一段代碼。
const p1 = new Promise((resolved, rejected) => {});console.log('p1', p1); //pending在以上的這段代碼中,控制臺(tái)打印結(jié)果如下。
在這段代碼中, p1 函數(shù)里面沒(méi)有內(nèi)容可以執(zhí)行,所以一直在等待狀態(tài),因此是 pending 。
2)演示2
const p2 = new Promise((resolved, rejected) => {setTimeout(() => {resolved();}); });console.log('p2', p2); //pending 一開(kāi)始打印時(shí) setTimeout(() => console.log('p2-setTimeout', p2)); //fulfilled在以上的這段代碼中,控制臺(tái)打印結(jié)果如下。
在這段代碼中, p2 一開(kāi)始打印的是 pending 狀態(tài),因?yàn)樗鼪](méi)有執(zhí)行到 setTimeout 里面。等到后續(xù)執(zhí)行 setTimeout 時(shí),才會(huì)觸發(fā)到 resolved 函數(shù),觸發(fā)后返回一個(gè) fulfilled 狀態(tài) promise 。
3)演示3
const p3 = new Promise((resolved, rejected) => {setTimeout(() => {rejected();}); });console.log('p3', p3); setTimeout(() => console.log('p3-setTimeout', p3)); //rejected在以上的這段代碼中,控制臺(tái)打印結(jié)果如下。
在這段代碼中, p3 一開(kāi)始打印的是 pending 狀態(tài),因?yàn)樗鼪](méi)有執(zhí)行到 setTimeout 里面。等到后續(xù)執(zhí)行 setTimeout 時(shí),同樣地,會(huì)觸發(fā)到 rejected 函數(shù),觸發(fā)后返回一個(gè) rejected 狀態(tài)的 promise 。
看完 promise 狀態(tài)的變化后,相信大家對(duì) promise 的三種狀態(tài)分別在什么時(shí)候觸發(fā)會(huì)有一定的了解。那么我們接下來(lái)繼續(xù)看 promise 狀態(tài)的表現(xiàn)。
(2)狀態(tài)的表現(xiàn)
- pending 狀態(tài),不會(huì)觸發(fā) then 和 catch 。
- fulfilled 狀態(tài),會(huì)觸發(fā)后續(xù)的 then 回調(diào)函數(shù)。
- rejected 狀態(tài),會(huì)觸發(fā)后續(xù)的 catch 回調(diào)函數(shù)。
我們來(lái)演示一下。
1)演示1
const p1 = Promise.resolve(100); //fulfilled console.log('p1', p1); p1.then(data => {console.log('data', data); }).catch(err => {console.error('err', err); });在以上的這段代碼中,控制臺(tái)打印結(jié)果如下。
在這段代碼中, p1 調(diào)用 promise 中的 resolved 回調(diào)函數(shù),此時(shí)執(zhí)行時(shí), p1 屬于 fulfilled 狀態(tài), fulfilled 狀態(tài)下,只會(huì)觸發(fā) .then 回調(diào)函數(shù),不會(huì)觸發(fā) .catch ,所以最終打印出 data 100 。
2)演示2
const p2 = Promise.reject('404'); //rejected console.log('p2', p2); p2.then(data => {console.log('data2', data); }).catch(err => {console.log('err2', err); })在以上的這段代碼中,控制臺(tái)打印結(jié)果如下。
在這段代碼中, p2 調(diào)用 promise 中的 reject 回調(diào)函數(shù),此時(shí)執(zhí)行時(shí), p1 屬于 reject 狀態(tài), reject 狀態(tài)下,只會(huì)觸發(fā) .catch 回調(diào)函數(shù),不會(huì)觸發(fā) .then ,所以最終打印出 err2 404 。
對(duì)三種狀態(tài)有了基礎(chǔ)了解之后,我們來(lái)深入了解 .then 和 .catch 對(duì)狀態(tài)的影響。
3、then和catch對(duì)狀態(tài)的影響(重要)
- then 正常返回 fulfilled ,里面有報(bào)錯(cuò)則返回 rejected ;
- catch 正常返回 fulfilled ,里面有報(bào)錯(cuò)則返回 rejected 。
我們先來(lái)看第一條規(guī)則: then 正常返回 fulfilled ,里面有報(bào)錯(cuò)則返回 rejected 。
1)演示1
const p1 = Promise.resolve().then(() => {return 100; }) console.log('p1', p1); //fulfilled狀態(tài),會(huì)觸發(fā)后續(xù)的.then回調(diào) p1.then(() => {console.log('123'); });在以上的這段代碼中,控制臺(tái)打印結(jié)果如下。
在這段代碼中, p1 調(diào)用 promise 中的 resolve 回調(diào)函數(shù),此時(shí)執(zhí)行時(shí), p1 正常返回 fulfilled , 不報(bào)錯(cuò),所以最終打印出 123 。
2)演示2
const p2 = Promise.resolve().then(() => {throw new Error('then error'); }); console.log('p2', p2); //rejected狀態(tài),觸發(fā)后續(xù).catch回調(diào) p2.then(() => {console.log('456'); }).catch(err => {console.error('err404', err); });在以上的這段代碼中,控制臺(tái)打印結(jié)果如下。
在這段代碼中, p2 調(diào)用 promise 中的 resolve 回調(diào)函數(shù),此時(shí)執(zhí)行時(shí), p2 在執(zhí)行過(guò)程中,拋出了一個(gè) Error ,所以,里面有報(bào)錯(cuò)則返回 rejected 狀態(tài) , 所以最終打印出 err404 Error: then error 的結(jié)果。
我們?cè)賮?lái)看第二條規(guī)則: catch 正常返回 fulfilled ,里面有報(bào)錯(cuò)則返回 rejected 。
1)演示1(需特別謹(jǐn)慎! !)
const p3 = Promise.reject('my error').catch(err => {console.error(err); }); console.log('p3', p3); //fulfilled狀態(tài),注意!觸發(fā)后續(xù).then回調(diào) p3.then(() => {console.log(100); });在以上的這段代碼中,控制臺(tái)打印結(jié)果如下。
在這段代碼中, p3 調(diào)用 promise 中的 rejected 回調(diào)函數(shù),此時(shí)執(zhí)行時(shí), p3 在執(zhí)行過(guò)程中,正常返回了一個(gè) Error ,這個(gè)點(diǎn)需要特別謹(jǐn)慎!!這看起來(lái)似乎有點(diǎn)違背常理,但對(duì)于 promise 來(lái)說(shuō),不管時(shí)調(diào)用 resolved 還是 rejected ,只要是正常返回而沒(méi)有拋出異常,都是返回 fulfilled 狀態(tài)。所以,最終 p3 的狀態(tài)是 fulfilled 狀態(tài),且因?yàn)槭?fulfilled 狀態(tài),之后還可以繼續(xù)調(diào)用 .then 函數(shù)。
2)演示2
const p4 = Promise.reject('my error').catch(err => {throw new Error('catch err'); }); console.log('p4', p4); //rejected狀態(tài),觸發(fā).catch回調(diào)函數(shù) p4.then(() => {console.log(200); }).catch(() => {console.log('some err'); });在以上的這段代碼中,控制臺(tái)打印結(jié)果如下。
在這段代碼中, p4 依然調(diào)用 promise 中的 reject 回調(diào)函數(shù),此時(shí)執(zhí)行時(shí), p4 在執(zhí)行過(guò)程中,拋出了一個(gè) Error ,所以,里面有報(bào)錯(cuò)則返回 rejected 狀態(tài) , 此時(shí) p4 的狀態(tài)為 rejected ,之后觸發(fā)后續(xù)的 .catch 回調(diào)函數(shù)。所以最終打印出 some err 的結(jié)果。
4、then和catch的鏈?zhǔn)秸{(diào)用(常考)
學(xué)習(xí)完以上知識(shí)后,我們通過(guò)幾道題來(lái)再總結(jié)回顧一下。
第一題:
Promise.resolve().then(() => {console.log(1); }).catch(() => {console.log(2); }).then(() => {console.log(3); });這道題打印的是 1 和 3 ,因?yàn)檎{(diào)用的是 promise 的 resolve 函數(shù),所以后續(xù)不會(huì)觸發(fā) .catch 函數(shù)。
第二題:
Promise.resolve().then(() => {console.log(1);throw new Error('error'); }).catch(() => {console.log(2); }).then(() => {console.log(3); });這道題打印的是 1 和 2 ,雖然調(diào)用的是 promise 的 resolve 函數(shù),但是中間拋出了一個(gè)異常,所以此時(shí) promise 變?yōu)?rejected 狀態(tài),所以后續(xù)不會(huì)觸發(fā) .then 函數(shù)。
第三題:
Promise.resolve().then(() => {console.log(1);throw new Error('error'); }).catch(() => {console.log(2); }).catch(() => { //這里是catchconsole.log(3); });這道題打印的是 1 和 2 和 3 ,跟第二題一樣,中間拋出了一個(gè)異常,所以此時(shí) promise 變?yōu)?rejected 狀態(tài),所以后續(xù)只觸發(fā) .catch 函數(shù)。
三、async/await
現(xiàn)代 js 的異步開(kāi)發(fā),基本上被 async 和 await 給承包和普及了。雖然說(shuō) promise 中的 .then 和 .catch 已經(jīng)很簡(jiǎn)潔了,但是 async 更簡(jiǎn)潔,它可以通過(guò)寫(xiě)同步代碼來(lái)執(zhí)行異步的效果。如此神奇的 async 和 await 究竟是什么呢?讓我們一起來(lái)一探究竟吧!
1、引例闡述
先用一個(gè)例子來(lái)展示 promise 和 async/await 的區(qū)別。假設(shè)我們現(xiàn)在要用異步來(lái)實(shí)現(xiàn)加載圖片。
(1) 如果用 promise 的 .then 和 .catch 實(shí)現(xiàn)時(shí),代碼如下:
function loadImg(src){const picture = new Promise((resolve, reject) => {const img = document.createElement('img');img.onload = () => {resolve(img);}img.onerror = () => {const err = new Error(`圖片加載失敗 ${src}`);reject(err);}img.src = src;})return picture; }const url1 = 'https://dss1.baidu.com/6ONXsjip0QIZ8tyhnq/it/u=2144980717,2336175712&fm=58'; const url2 = 'https://dss1.baidu.com/6ONXsjip0QIZ8tyhnq/it/u=614367910,2483275034&fm=58';loadImg(url1).then(img1 => {console.log(img1.width);return img1; //普通對(duì)象 }).then(img1 => {console.log(img1.height);return loadImg(url2); //promise 實(shí)例 }).then(img2 => {console.log(img2.width);return img2; }).then(img2 => {console.log(img2.height); }).catch(ex => console.error(ex));(2) 如果用 async 實(shí)現(xiàn)時(shí),代碼如下:
function loadImg(src){const picture = new Promise((resolve, reject) => {const img = document.createElement('img');img.onload = () => {resolve(img);}img.onerror = () => {const err = new Error(`圖片加載失敗 ${src}`);reject(err);}img.src = src;})return picture; }const url1 = 'https://dss1.baidu.com/6ONXsjip0QIZ8tyhnq/it/u=2144980717,2336175712&fm=58'; const url2 = 'https://dss2.bdstatic.com/8_V1bjqh_Q23odCf/pacific/1990552278.jpg';!(async function () {// img1const img1 = await loadImg(url1);console.log(img1.width, img1.height);// img2const img2 = await loadImg(url2);console.log(img2.width, img2.height); })();大家可以看到,如果用第二種方式的話(huà),代碼要優(yōu)雅許多。且最關(guān)鍵的是,通過(guò) async 和 await ,用同步代碼就可以實(shí)現(xiàn)異步的功能。接下來(lái)我們開(kāi)始來(lái)了解 async 和 await 。
2、async和await
- 背景:解決異步回調(diào)問(wèn)題,防止深陷回調(diào)地獄 Callback hell ;
- Promise :Promise then catch 是鏈?zhǔn)秸{(diào)用,但也是基于回調(diào)函數(shù);
- async/await :async/await 是同步語(yǔ)法,徹底消滅回調(diào)函數(shù),是消滅異步回調(diào)的終極武器。
3、async/await和promise的關(guān)系
(1) async/await 與 promise 并不互斥,兩者相輔相成。
(2) 執(zhí)行 async 函數(shù),返回的是 promise 對(duì)象。
(3) await 相當(dāng)于 promise 的 then 。
(4) try…catch 可以捕獲異常,代替了 promise 的 catch 。
接下來(lái)我們來(lái)一一(2)(3)(4)演示這幾條規(guī)則。
第一條規(guī)則: 執(zhí)行 async 函數(shù),返回的是 promise 對(duì)象。
async function fn1(){return 100; //相當(dāng)于return promise.resolve(100); }const res1 = fn1(); //執(zhí)行async函數(shù),返回的是一個(gè)Promise對(duì)象 console.log('res1', res1); //promise對(duì)象 res1.then(data => {console.log('data', data); //100 });在以上的這段代碼中,控制臺(tái)打印結(jié)果如下。
大家可以看到,第一個(gè) res1 返回的是一個(gè) promise 對(duì)象,且此時(shí) promise 對(duì)象的狀態(tài)是 fulfilled 狀態(tài),所以可以調(diào)用后續(xù)的 .then 并且打印出 data 100 。
第二條規(guī)則: await 相當(dāng)于 promise 的 then 。
!(async function (){const p1 = Promise.resolve(300);const data = await p1; //await 相當(dāng)于 promise的thenconsole.log('data', data); //data 300 })();!(async function () {const data1 = await 400; //await Promise.resolve(400)console.log('data1', data1); //data1 400 })();在以上的這段代碼中,控制臺(tái)打印結(jié)果如下。
大家可以看到, p1 調(diào)用 resolve 回調(diào)函數(shù),所以此時(shí) p1 屬于 fulfilled 狀態(tài),之后 const data = await p1 中的await,相當(dāng)于 promise 的 then ,又因?yàn)榇藭r(shí) p1 屬于 fulfilled 狀態(tài),所以可以對(duì) .then 進(jìn)行調(diào)用,于是輸出 data 300 。同理在第二段代碼中, await 400 時(shí), 400 即表示 Promise.resolved(400) ,因此屬于 fulfilled 狀態(tài),隨后調(diào)用 .then ,打印出 data1 400 結(jié)果。
再來(lái)看一段代碼:
!(async function (){const p2 = Promise.reject('err1');const res = await p4; //await 相當(dāng)于 promise的thenconsole.log('res', res); //不打印 })();在以上的這段代碼中,控制臺(tái)打印結(jié)果如下。
大家可以看到, p2 調(diào)用 reject 回調(diào)函數(shù),所以此時(shí) p2 屬于 reject 狀態(tài)。但因?yàn)閍wait是觸發(fā) promise 中的 .then ,所以此時(shí) res 不會(huì)被觸發(fā),于是后續(xù)不會(huì)對(duì)await進(jìn)行操作,控制臺(tái)也就不對(duì) console.log('res', res); 進(jìn)行打印。
第三條規(guī)則: try…catch 可以捕獲異常,代替了 promise 的 catch 。
!(async function () {const p3 = Promise.reject('err1'); //rejected 狀態(tài)try{const res = await p3;console.log(res);}catch(ex){console.error(ex); //try…catch 相當(dāng)于 promise的catch} })();在以上的這段代碼中,控制臺(tái)打印結(jié)果如下。
大家可以看到, p3 調(diào)用 reject 回調(diào)函數(shù),所以此時(shí) p3 屬于 rejected 狀態(tài),因此它不會(huì)執(zhí)行 try 的內(nèi)容,而是去執(zhí)行 catch 的內(nèi)容, try…catch 中的 catch 就相當(dāng)于 promise 中的 catch ,且此時(shí) p3 屬于 rejected 狀態(tài),因此執(zhí)行 catch ,瀏覽器捕獲到異常,報(bào)出錯(cuò)誤。
4、異步的本質(zhì)
從上面的分析中,不管是 promise 還是 async/await ,都是解決異步問(wèn)題。但是呢,異步的本質(zhì)還是解決同步的問(wèn)題,所以,異步的本質(zhì)是:
- async/await 是消滅異步回調(diào)的終極武器;
- JS 是單線(xiàn)程的,需要有異步,需要基于 event loop ;
- async/await 是一個(gè)語(yǔ)法糖,但是這顆糖非常好用!!
我們來(lái)看兩道 async/await 的順序問(wèn)題,回顧 async/await 。
第一題:
async function async1(){console.log('async start'); // 2await async2();//await 的后面,都可以看做是callback里面的內(nèi)容,即異步。//類(lèi)似event loop//Promise.resolve().then(() => console.log('async1 end'))console.log('async1 end'); // 5 }async function async2(){console.log('async2'); //3 }console.log('script start'); // 1 async1(); console.log('script end'); // 4在以上的這段代碼中,控制臺(tái)打印結(jié)果如下。
從上面這段代碼中可以看到,先執(zhí)行同步代碼 1 ,之后執(zhí)行回調(diào)函數(shù) async1() ,在回調(diào)函數(shù) async1() 當(dāng)中,先執(zhí)行同步代碼 2 ,之后遇到 await ,值得注意的是, await 的后面,都可以看作是 callback 里面的內(nèi)容,即異步內(nèi)容,所以,先執(zhí)行 await 中對(duì)應(yīng)的 async2() 里面的內(nèi)容,之后把 await 后面所有的內(nèi)容放置到異步當(dāng)中。繼續(xù)執(zhí)行 4 ,等到 4 執(zhí)行完時(shí),整個(gè)同步代碼已經(jīng)執(zhí)行完,最后,再去執(zhí)行異步的代碼,最終輸出 5 的內(nèi)容。
同樣的方式來(lái)來(lái)分析第二題。
第二題:
async function async1(){console.log('async1 start'); //2await async2();// 下面三行都是異步回調(diào),callback的內(nèi)容console.log('async1 end'); //5await async3();// 下面一行是回調(diào)的內(nèi)容,相當(dāng)于異步回調(diào)里面再嵌套一個(gè)異步回調(diào)。console.log('async1 end 2'); //7 }async function async2(){console.log('async2'); //3 }async function async3(){console.log('async3'); //6 }console.log('script start'); //1 async1(); console.log('script end'); //4在以上的這段代碼中,控制臺(tái)打印結(jié)果如下。
這里就不再進(jìn)行分析啦!大家可以根據(jù)第一個(gè)案例的步驟進(jìn)行分析。
5、場(chǎng)景題
最后的最后,我們?cè)賮?lái)做兩道題回顧我們剛剛講過(guò)的 async/await 知識(shí)點(diǎn)。
(1)async/await語(yǔ)法
async function fn(){return 100; } (async function(){const a = fn(); //?? Promiseconst b = await fn(); //?? 100 })(); (async function(){console.log('start');const a = await 100;console.log('a', a);const b = await Promise.resolve(200);console.log('b', b);const c = await Promise.reject(300); //出錯(cuò)了,再往后都不會(huì)執(zhí)行了console.log('c', c);console.log('end'); })(); //執(zhí)行完畢,打印出哪些內(nèi)容? //start //100 //200(2)async/await的順序問(wèn)題
async function async1(){console.log('async1 start'); // 2await async2();//await后面的都作為回調(diào)內(nèi)容 —— 微任務(wù)console.log('async1 end'); // 6 }async function async2(){console.log('async2'); // 3 }console.log('script start'); // 1setTimeout(function(){ //宏任務(wù) —— setTimeoutconsole.log('setTimeout'); // 8 }, 0);//遇到函數(shù),立馬去執(zhí)行函數(shù) async1();//初始化promise時(shí),傳入的函數(shù)會(huì)立刻被執(zhí)行 new Promise(function(resolve){ //promise —— 微任務(wù)console.log('promise1'); // 4resolve(); }).then(function(){ //微任務(wù)console.log('promise2'); // 7 });console.log('script end'); // 5//同步代碼執(zhí)行完畢(event loop —— call stack 被清空) //執(zhí)行微任務(wù) //(嘗試觸發(fā) DOM 渲染) // 觸發(fā)event loop,執(zhí)行宏任務(wù)這里就不再進(jìn)行一一解析啦!大家可以前面知識(shí)點(diǎn)的學(xué)習(xí)總計(jì)再回顧理解。
四、寫(xiě)在最后
關(guān)于 js 的異步問(wèn)題以及異步的解決方案問(wèn)題就講到這里啦!u1s1, promise 和 async/await 在我們?nèi)粘5那岸碎_(kāi)發(fā)中還是蠻重要的,基本上寫(xiě)異步代碼時(shí)候都會(huì)用到 async/await 來(lái)解決。啃了16個(gè)小時(shí)總結(jié)了event loop 和 promise 、async/await 問(wèn)題,希望對(duì)大家有幫助。
同時(shí),里面可能有一些講的不好或者不容易理解的地方也歡迎評(píng)論區(qū)評(píng)論或私信我交流~
關(guān)注公眾號(hào) 星期一研究室 ,不定期分享學(xué)習(xí)干貨~
如果這篇文章對(duì)你有用,記得點(diǎn)個(gè)贊加個(gè)關(guān)注哦~
總結(jié)
以上是生活随笔為你收集整理的解决异步问题,教你如何写出优雅的promise和async/await,告别callback回调地狱!的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: 如何在拼多多查看好友列表
- 下一篇: 你真的理解事件绑定、事件冒泡和事件委托吗