电脑知识|欧美黑人一区二区三区|软件|欧美黑人一级爽快片淫片高清|系统|欧美黑人狂野猛交老妇|数据库|服务器|编程开发|网络运营|知识问答|技术教程文章 - 好吧啦网

您的位置:首頁技術文章
文章詳情頁

JavaScript 異步進化史

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

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

JavaScript 異步進化史

同步與異步

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

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

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

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

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

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

callback

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

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

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,這就是非線性代碼帶來的糟糕之處。

通過將ajax后面執(zhí)行的任務提前,可以更容易看懂代碼的執(zhí)行順序。雖然代碼因為嵌套看起來不美觀,但現(xiàn)在的執(zhí)行順序卻是從上到下的“線性”方式。這種技巧在寫多重嵌套的代碼時,是非常有用的。

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

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

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)導致的“非線性”的問題,制定了錯誤優(yōu)先的策略。node 中 callback 的第一個參數(shù),專門用于判斷是否發(fā)生異常

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

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

promise

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

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

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

Promise 是個非常不錯的中介,它只返回可信的信息給 callback。它對第三方異步庫的結果進行了一些加工,保證了 callback 一定會被異步調(diào)用,且只會被調(diào)用一次。

var promise1 = new Promise(function (resolve) { // 可能由于某些原因導致同步調(diào)用 resolve('B');});// promise依舊會異步執(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只會調(diào)用一次promise2.then(function(){ console.log('A')});// A (只有一個)var promise3 = new Promise(function (resolve,reject) { // 成功回調(diào)先被通知,又通知了失敗回調(diào) setTimeout(function(){ resolve(); reject(); },0)});// promise只會調(diào)用成功回調(diào)promise3.then(function(){ console.log('A')}).catch(function(){ console.log('B')});// A(只有A)

介紹完 Promise 的特性后,來看看它如何利用鏈式調(diào)用,解決異步代碼可讀性的問題的。

var fetch = function(url){ // 返回一個新的 Promise 實例 return new Promise(function (resolve,reject) {ajax(url,resolve,reject); });}A();fetch('url1').then(function(){ B(); // 返回一個新的 Promise 實例 return fetch('url2');}).catch(function(){ // 異常的時候也可以返回一個新的 Promise 實例 return fetch('url2'); // 使用鏈式寫法調(diào)用這個新的 Promise 實例的 then 方法 }).then(function() { C(); // 繼續(xù)返回一個新的 Promise 實例...})// A B C ...

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

Promise 解決的另外一個難點是 callback 只能捕獲當前錯誤異常。Promise 和 callback 不同,每個 callback 只能知道自己的報錯情況,但 Promise 代理著所有的 callback,所有 callback 的報錯,都可以由 Promise 統(tǒng)一處理。所以,可以通過catch來捕獲之前未捕獲的異常。

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

異步(async)函數(shù)

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

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

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

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

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

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ù)也可以被當作值,傳入普通函數(shù)和異步函數(shù)中執(zhí)行。但是在異步函數(shù)中,使用異步函數(shù)時要注意,如果不使用await,異步函數(shù)會被同步執(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

這個時候打印出來的值是 B A。說明 doAsync 函數(shù)并沒有等待 main 的異步執(zhí)行完畢就執(zhí)行了 console。如果要讓 console 在 main 的異步執(zhí)行完畢后才執(zhí)行,我們需要在main前添加關鍵字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ù)采用類同步的書寫方法,所以在處理多個并發(fā)請求,新手可能會像下面一樣書寫。這樣會導致url2的請求必需等到url1的請求回來后才會發(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的方法來解決這個問題。Promise.all用于將多個Promise實例,包裝成一個新的 Promis e實例,當所有的 Promise 成功后才會觸發(fā)Promise.all的resolve函數(shù),當有一個失敗,則立即調(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ù)的轉碼了,大家可以在自己的項目中開始嘗試。

標簽: JavaScript
相關文章:
主站蜘蛛池模板: 重庆监控_电子围栏设备安装公司_门禁停车场管理系统-劲浪科技公司 | 碳钢法兰厂家,非标法兰,定制异型,法兰生产厂家-河北九瑞管道 | 衡阳耐适防护科技有限公司——威仕盾焊接防护用品官网/焊工手套/焊接防护服/皮革防护手套 | 武汉高低温试验机-现货恒温恒湿试验箱-高低温湿热交变箱价格-湖北高天试验设备 | 通用磨耗试验机-QUV耐候试验机|久宏实业百科 | 泡沫消防车_水罐消防车_湖北江南专用特种汽车有限公司 | 河南包装袋厂家_河南真空袋批发价格_河南服装袋定制-恒源达包装制品 | 生物风-销售载体,基因,质粒,ATCC细胞,ATCC菌株等,欢迎购买-百风生物 | 东风体检车厂家_公共卫生体检车_医院体检车_移动体检车-锦沅科贸 | 小型数控车床-数控车床厂家-双头数控车床 | 厂房出售_厂房仓库出租_写字楼招租_土地出售-中苣招商网-中苣招商网 | 工业硝酸钠,硝酸钠厂家-淄博「文海工贸」 | 胶泥瓷砖胶,轻质粉刷石膏,嵌缝石膏厂家,腻子粉批发,永康家德兴,永康市家德兴建材厂 | 鲸鱼视觉 -数字展厅多媒体互动展示制作公司 | 重庆网站建设,重庆网站设计,重庆网站制作,重庆seo,重庆做网站,重庆seo,重庆公众号运营,重庆小程序开发 | 建筑消防设施检测系统检测箱-电梯**检测仪器箱-北京宇成伟业科技有限责任公司 | nalgene洗瓶,nalgene量筒,nalgene窄口瓶,nalgene放水口大瓶,浙江省nalgene代理-杭州雷琪实验器材有限公司 | 翰墨AI智能写作助手官网_人工智能问答在线AI写作免费一键生成 | 喷砂机厂家_自动喷砂机生产_新瑞自动化喷砂除锈设备 | 德国进口电锅炉_商用电热水器_壁挂炉_电采暖器_电热锅炉[德国宝] | 广东青藤环境科技有限公司-水质检测 | 佛山商标注册_商标注册代理|专利注册申请_商标注册公司_鸿邦知识产权 | 中天寰创-内蒙古钢结构厂家|门式刚架|钢结构桁架|钢结构框架|包头钢结构煤棚 | 北京租车公司_汽车/客车/班车/大巴车租赁_商务会议/展会用车/旅游大巴出租_北京桐顺创业租车公司 | 大倾角皮带机-皮带输送机-螺旋输送机-矿用皮带输送机价格厂家-河南坤威机械 | 噪声治理公司-噪音治理专业隔音降噪公司 | 蒸压釜-陶粒板隔墙板蒸压釜-山东鑫泰鑫智能装备有限公司 | 板式换网器_柱式换网器_自动换网器-郑州海科熔体泵有限公司 | 云南成人高考_云南成考网| CE认证_FCC认证_CCC认证_MFI认证_UN38.3认证-微测检测 CNAS实验室 | 工业车间焊接-整体|集中除尘设备-激光|等离子切割机配套除尘-粉尘烟尘净化治理厂家-山东美蓝环保科技有限公司 | 国资灵活用工平台_全国灵活用工平台前十名-灵活用工结算小帮手 | 渣油泵,KCB齿轮泵,不锈钢齿轮泵,重油泵,煤焦油泵,泊头市泰邦泵阀制造有限公司 | 螺杆式冷水机-低温冷水机厂家-冷冻机-风冷式-水冷式冷水机-上海祝松机械有限公司 | 右手官网|右手工业设计|外观设计公司|工业设计公司|产品创新设计|医疗产品结构设计|EMC产品结构设计 | 月嫂_保姆_育婴_催乳_母婴护理_产后康复_养老护理-吉祥到家家政 硫酸亚铁-聚合硫酸铁-除氟除磷剂-复合碳源-污水处理药剂厂家—长隆科技 | 全自动翻转振荡器-浸出式水平振荡器厂家-土壤干燥箱价格-常州普天仪器 | GAST/BRIWATEC/CINCINNATI/KARL-KLEIN/ZIEHL-ABEGG风机|亚喜科技 | 锡膏喷印机-全自动涂覆机厂家-全自动点胶机-视觉点胶机-深圳市博明智控科技有限公司 | 自动检重秤-动态称重机-重量分选秤-苏州金钻称重设备系统开发有限公司 | MOOG伺服阀维修,ATOS比例流量阀维修,伺服阀维修-上海纽顿液压设备有限公司 |