国产成人精品久久免费动漫-国产成人精品天堂-国产成人精品区在线观看-国产成人精品日本-a级毛片无码免费真人-a级毛片毛片免费观看久潮喷

您的位置:首頁(yè)技術(shù)文章
文章詳情頁(yè)

JavaScript 異步進(jìn)化史

瀏覽:14日期:2023-11-17 16:33:43
前言

JS 中最基礎(chǔ)的異步調(diào)用方式是 callback,它將回調(diào)函數(shù) callback 傳給異步 API,由瀏覽器或 Node 在異步完成后,通知 JS 引擎調(diào)用 callback。對(duì)于簡(jiǎn)單的異步操作,用 callback 實(shí)現(xiàn),是夠用的。但隨著負(fù)責(zé)交互頁(yè)面和 Node 出現(xiàn),callback 方案的弊端開始浮現(xiàn)出來(lái)。 Promise 規(guī)范孕育而生,并被納入 ES6 的規(guī)范中。后來(lái) ES7 又在 Promise 的基礎(chǔ)上將 async 函數(shù)納入標(biāo)準(zhǔn)。此為 JavaScript 異步進(jìn)化史。

JavaScript 異步進(jìn)化史

同步與異步

通常,代碼是由上往下依次執(zhí)行的。如果有多個(gè)任務(wù),就必需排隊(duì),前一個(gè)任務(wù)完成,后一個(gè)任務(wù)才會(huì)執(zhí)行。這種執(zhí)行模式稱之為:同步(synchronous)。新手容易把計(jì)算機(jī)用語(yǔ)中的同步,和日常用語(yǔ)中的同步弄混淆。如,“把文件同步到云端”中的同步,指的是“使...保持一致”。而在計(jì)算機(jī)中,同步指的是任務(wù)從上往下依次執(zhí)行的模式。比如:

A();B();C();

在這段代碼中,A、B、C是三個(gè)不同的函數(shù),每個(gè)函數(shù)都是一個(gè)不相關(guān)的任務(wù)。在同步模式,計(jì)算機(jī)會(huì)先執(zhí)行 A 任務(wù),再執(zhí)行 B 任務(wù),最后執(zhí)行 C 任務(wù)。在大部分情況,同步模式都沒(méi)問(wèn)題。但是如果 B 任務(wù)是一個(gè)耗時(shí)很長(zhǎng)的網(wǎng)絡(luò)請(qǐng)求,而 C 任務(wù)恰好是展現(xiàn)新頁(yè)面,就會(huì)導(dǎo)致網(wǎng)頁(yè)卡頓。

更好解決方案是,將 B 任務(wù)分成兩個(gè)部分。一部分立即執(zhí)行網(wǎng)絡(luò)請(qǐng)求的任務(wù),另一部分在請(qǐng)求回來(lái)后的執(zhí)行任務(wù)。這種一部分立即執(zhí)行,另一部分在未來(lái)執(zhí)行的模式稱為異步。

A();// 在現(xiàn)在發(fā)送請(qǐng)求 ajax('url1',function B() { // 在未來(lái)某個(gè)時(shí)刻執(zhí)行})C();// 執(zhí)行順序 A => C => B

實(shí)際上,JS 引擎并沒(méi)有直接處理網(wǎng)絡(luò)請(qǐng)求的任務(wù),它只是調(diào)用了瀏覽器的網(wǎng)絡(luò)請(qǐng)求接口,由瀏覽器發(fā)送網(wǎng)絡(luò)請(qǐng)求并監(jiān)聽返回的數(shù)據(jù)。JavaScript 異步能力的本質(zhì)是瀏覽器或 Node 的多線程能力。

callback

未來(lái)執(zhí)行的函數(shù)通常也叫 callback。使用 callback 的異步模式,解決了阻塞的問(wèn)題,但是也帶來(lái)了一些其他問(wèn)題。在最開始,我們的函數(shù)是從上往下書寫的,也是從上往下執(zhí)行的,這種“線性”模式,非常符合我們的思維習(xí)慣,但是現(xiàn)在卻被 callback 打斷了!在上面一段代碼中,現(xiàn)在它跳過(guò) B 任務(wù)先執(zhí)行了 C任務(wù)!這種異步“非線性”的代碼會(huì)比同步“線性”的代碼,更難閱讀,因此也更容易滋生 BUG。

試著判斷下面這段代碼的執(zhí)行順序,你會(huì)對(duì)“非線性”代碼比“線性”代碼更難以閱讀,體會(huì)更深。

A();ajax('url1', function(){ B(); ajax('url2', function(){C(); } D(); });E();// A => E => B => D => C

這段代碼中,從上往下執(zhí)行的順序被 Callback 打亂了。我們的閱讀代碼視線是A => B => C => D => E,但是執(zhí)行順序卻是A => E => B => D => C,這就是非線性代碼帶來(lái)的糟糕之處。

通過(guò)將ajax后面執(zhí)行的任務(wù)提前,可以更容易看懂代碼的執(zhí)行順序。雖然代碼因?yàn)榍短卓雌饋?lái)不美觀,但現(xiàn)在的執(zhí)行順序卻是從上到下的“線性”方式。這種技巧在寫多重嵌套的代碼時(shí),是非常有用的。

A();E();ajax('url1', function(){ B(); D(); ajax('url2', function(){C(); } });// A => E => B => D => C

上一段代碼只有處理了成功回調(diào),并沒(méi)處理異常回調(diào)。接下來(lái),把異常處理回調(diào)加上,再來(lái)討論代碼“線性”執(zhí)行的問(wèn)題。

A();ajax('url1', function(){ B(); ajax('url2', function(){C(); },function(){D(); }); },function(){ E(); });

加上異常處理回調(diào)后,url1的成功回調(diào)函數(shù) B 和異?;卣{(diào)函數(shù) E,被分開了。這種“非線性”的情況又出現(xiàn)了。

在 node 中,為了解決的異?;卣{(diào)導(dǎo)致的“非線性”的問(wèn)題,制定了錯(cuò)誤優(yōu)先的策略。node 中 callback 的第一個(gè)參數(shù),專門用于判斷是否發(fā)生異常

A();get('url1', function(error){ if(error){E(); }else {B();get('url2', function(error){ if(error){D(); }else{C(); }}); }});

到此,callback 引起的“非線性”問(wèn)題基本得到解決。遺憾的是,使用 callback 嵌套,一層層if else和回調(diào)函數(shù),一旦嵌套層數(shù)多起來(lái),閱讀起來(lái)不是很方便。此外,callback 一旦出現(xiàn)異常,只能在當(dāng)前回調(diào)函數(shù)內(nèi)部處理異常。

promise

在 JavaScript 的異步進(jìn)化史中,涌現(xiàn)出一系列解決 callback 弊端的庫(kù),而 Promise 成為了最終的勝者,并成功地被引入了 ES6 中。它將提供了一個(gè)更好的“線性”書寫方式,并解決了異步異常只能在當(dāng)前回調(diào)中被捕獲的問(wèn)題。

Promise 就像一個(gè)中介,它承諾會(huì)將一個(gè)可信任的異步結(jié)果返回。首先 Promise 和異步接口簽訂一個(gè)協(xié)議,成功時(shí),調(diào)用resolve函數(shù)通知 Promise,異常時(shí),調(diào)用reject通知 Promise。另一方面 Promise 和 callback 也簽訂一個(gè)協(xié)議,由 Promise 在將來(lái)返回可信任的值給then和catch中注冊(cè)的 callback。

// 創(chuàng)建一個(gè) Promise 實(shí)例(異步接口和 Promise 簽訂協(xié)議)var promise = new Promise(function (resolve,reject) { ajax('url',resolve,reject);});// 調(diào)用實(shí)例的 then catch 方法 (成功回調(diào)、異常回調(diào)與 Promise 簽訂協(xié)議)promise.then(function(value) { // success}).catch(function (error) { // error})

Promise 是個(gè)非常不錯(cuò)的中介,它只返回可信的信息給 callback。它對(duì)第三方異步庫(kù)的結(jié)果進(jìn)行了一些加工,保證了 callback 一定會(huì)被異步調(diào)用,且只會(huì)被調(diào)用一次。

var promise1 = new Promise(function (resolve) { // 可能由于某些原因?qū)е峦秸{(diào)用 resolve('B');});// promise依舊會(huì)異步執(zhí)行promise1.then(function(value){ console.log(value)});console.log('A');// A B (先 A 后 B)var promise2 = new Promise(function (resolve) { // 成功回調(diào)被通知了2次 setTimeout(function(){ resolve(); },0)});// promise只會(huì)調(diào)用一次promise2.then(function(){ console.log('A')});// A (只有一個(gè))var promise3 = new Promise(function (resolve,reject) { // 成功回調(diào)先被通知,又通知了失敗回調(diào) setTimeout(function(){ resolve(); reject(); },0)});// promise只會(huì)調(diào)用成功回調(diào)promise3.then(function(){ console.log('A')}).catch(function(){ console.log('B')});// A(只有A)

介紹完 Promise 的特性后,來(lái)看看它如何利用鏈?zhǔn)秸{(diào)用,解決異步代碼可讀性的問(wèn)題的。

var fetch = function(url){ // 返回一個(gè)新的 Promise 實(shí)例 return new Promise(function (resolve,reject) {ajax(url,resolve,reject); });}A();fetch('url1').then(function(){ B(); // 返回一個(gè)新的 Promise 實(shí)例 return fetch('url2');}).catch(function(){ // 異常的時(shí)候也可以返回一個(gè)新的 Promise 實(shí)例 return fetch('url2'); // 使用鏈?zhǔn)綄懛ㄕ{(diào)用這個(gè)新的 Promise 實(shí)例的 then 方法 }).then(function() { C(); // 繼續(xù)返回一個(gè)新的 Promise 實(shí)例...})// A B C ...

如此反復(fù),不斷返回一個(gè) Promise 對(duì)象,再采用鏈?zhǔn)秸{(diào)用的方式不斷地調(diào)用。使 Promise 擺脫了 callback 層層嵌套的問(wèn)題和異步代碼“非線性”執(zhí)行的問(wèn)題。

Promise 解決的另外一個(gè)難點(diǎn)是 callback 只能捕獲當(dāng)前錯(cuò)誤異常。Promise 和 callback 不同,每個(gè) callback 只能知道自己的報(bào)錯(cuò)情況,但 Promise 代理著所有的 callback,所有 callback 的報(bào)錯(cuò),都可以由 Promise 統(tǒng)一處理。所以,可以通過(guò)catch來(lái)捕獲之前未捕獲的異常。

Promise 解決了 callback 的異步調(diào)用問(wèn)題,但 Promise 并沒(méi)有擺脫 callback,它只是將 callback 放到一個(gè)可以信任的中間機(jī)構(gòu),這個(gè)中間機(jī)構(gòu)去鏈接我們的代碼和異步接口。

異步(async)函數(shù)

異步(async)函數(shù)是 ES7 的一個(gè)新的特性,它結(jié)合了 Promise,讓我們擺脫 callback 的束縛,直接用類同步的“線性”方式,寫異步函數(shù)。

聲明異步函數(shù),只需在普通函數(shù)前添加一個(gè)關(guān)鍵字 async 即可,如async function main(){} 。在異步函數(shù)中,可以使用await關(guān)鍵字,表示等待后面表達(dá)式的執(zhí)行結(jié)果,一般后面的表達(dá)式是 Promise 實(shí)例。

async function main{ // timer 是在上一個(gè)例子中定義的 var value = await timer(100); console.log(value); // done (100ms 后返回 done)}main();

異步函數(shù)和普通函數(shù)一樣調(diào)用 main() 。調(diào)用后,會(huì)立即執(zhí)行異步函數(shù)中的第一行代碼 var value = await timer(100) 。等到異步執(zhí)行完成后,才會(huì)執(zhí)行下一行代碼。

除此之外,異步函數(shù)和其他函數(shù)基本類似,它使用try...catch來(lái)捕捉異常。也可以傳入?yún)?shù)。但不要在異步函數(shù)中使用return來(lái)返回值。

var timer = new Promise(function create(resolve,reject) { if(typeof delay !== 'number'){ reject(new Error('type error')); } setTimeout(resolve,delay,'done');});async function main(delay){ try{ var value1 = await timer(delay); var value2 = await timer(''); var value3 = await timer(delay); }catch(err){ console.error(err); // Error: type error // at create (<anonymous>:5:14) // at timer (<anonymous>:3:10) // at A (<anonymous>:12:10) }}main(0);

異步函數(shù)也可以被當(dāng)作值,傳入普通函數(shù)和異步函數(shù)中執(zhí)行。但是在異步函數(shù)中,使用異步函數(shù)時(shí)要注意,如果不使用await,異步函數(shù)會(huì)被同步執(zhí)行。

async function main(delay){ var value1 = await timer(delay); console.log('A')}async function doAsync(main){ main(0); console.log('B')}doAsync(main);// B A

這個(gè)時(shí)候打印出來(lái)的值是 B A。說(shuō)明 doAsync 函數(shù)并沒(méi)有等待 main 的異步執(zhí)行完畢就執(zhí)行了 console。如果要讓 console 在 main 的異步執(zhí)行完畢后才執(zhí)行,我們需要在main前添加關(guān)鍵字await。

async function main(delay){ var value1 = await timer(delay); console.log('A')}async function doAsync(main){ await main(0); console.log('B')}doAsync(main);// A B

由于異步函數(shù)采用類同步的書寫方法,所以在處理多個(gè)并發(fā)請(qǐng)求,新手可能會(huì)像下面一樣書寫。這樣會(huì)導(dǎo)致url2的請(qǐng)求必需等到url1的請(qǐng)求回來(lái)后才會(huì)發(fā)送。

var fetch = function (url) { return new Promise(function (resolve,reject) { ajax(url,resolve,reject); });}async function main(){ try{ var value1 = await fetch('url1'); var value2 = await fetch('url2'); conosle.log(value1,value2); }catch(err){ console.error(err) }}main();

使用Promise.all的方法來(lái)解決這個(gè)問(wèn)題。Promise.all用于將多個(gè)Promise實(shí)例,包裝成一個(gè)新的 Promis e實(shí)例,當(dāng)所有的 Promise 成功后才會(huì)觸發(fā)Promise.all的resolve函數(shù),當(dāng)有一個(gè)失敗,則立即調(diào)用Promise.all的reject函數(shù)。

var fetch = function (url) { return new Promise(function (resolve,reject) { ajax(url,resolve,reject); });}async function main(){ try{ var arrValue = await Promise.all[fetch('url1'),fetch('url2')]; conosle.log(arrValue[0],arrValue[1]); }catch(err){ console.error(err) }}main();

目前使用 Babel 已經(jīng)支持 ES7 異步函數(shù)的轉(zhuǎn)碼了,大家可以在自己的項(xiàng)目中開始嘗試。

標(biāo)簽: JavaScript
相關(guān)文章:
主站蜘蛛池模板: 久久国产成人午夜aⅴ影院 久久国产成人亚洲精品影院老金 | 亚洲人成在线精品 | 久久精品国产影库免费看 | 亚洲精品天堂在线观看 | 国产系列在线播放 | 免费观看成为人视频 | 久久香蕉精品成人 | 国产99视频精品免费视频免里 | 国产精品91在线播放 | 国产乱淫a∨片免费视频 | 日韩一级片在线观看 | 日韩专区亚洲综合久久 | 综合 91在线精品 | 午夜一级毛片看看 | 在线播放一区二区精品产 | 亚洲高清无在码在线无弹窗 | 波多野结衣aⅴ在线 | 手机日韩理论片在线播放 | 中文字幕欧美在线观看 | 在线观看国产精品一区 | 亚洲一区二区三区久久久久 | 精品国产理论在线观看不卡 | 久久久精品视频免费观看 | 免费人成黄页网站在线观看 | 久色网址| 成人看片黄a在线看 | 亚洲精品高清视频 | 欧美精品亚洲精品日韩 | 男女男精品视频网站 | 国产精品短视频免费观看 | 在线观看人成网站深夜免费 | 一级毛片成人免费看a | 亚洲视频aaa | 国内一级野外a一级毛片 | 日韩欧美在线一级一中文字暮 | 久久99国产精品久久 | 日本一区二区高清不卡 | 美国黑人特大一级毛片 | 国产免费一区二区在线看 | 国产精品久久国产精品99 | 亚洲精品一区最新 |