自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

Javascript異步編程詳解

開發(fā) 前端
Javascript語言將任務(wù)的執(zhí)行模式分成兩種:同步(Synchronous)和異步(Asynchronous)."異步模式"非常重要。在瀏覽器端,耗時(shí)很長(zhǎng)的操作都應(yīng)該異步執(zhí)行,避免瀏覽器失去響應(yīng),最好的例子就是Ajax操作。在服務(wù)器端,"異步模式"甚至是唯一的模式,因?yàn)閳?zhí)行環(huán)境是單線程的,如果允許同步執(zhí)行所有http請(qǐng)求,服務(wù)器性能會(huì)急劇下降,很快就會(huì)失去響應(yīng)。

前言

你可能知道,Javascript語言的執(zhí)行環(huán)境是"單線程"(single thread)。

所謂"單線程",就是指一次只能完成一件任務(wù)。如果有多個(gè)任務(wù),就必須排隊(duì),前面一個(gè)任務(wù)完成,再執(zhí)行后面一個(gè)任務(wù),以此類推。

這種模式的好處是實(shí)現(xiàn)起來比較簡(jiǎn)單,執(zhí)行環(huán)境相對(duì)單純;壞處是只要有一個(gè)任務(wù)耗時(shí)很長(zhǎng),后面的任務(wù)都必須排隊(duì)等著,會(huì)拖延整個(gè)程序的執(zhí)行。常見的瀏覽器無響應(yīng)(假死),往往就是因?yàn)槟骋欢蜫avascript代碼長(zhǎng)時(shí)間運(yùn)行(比如死循環(huán)),導(dǎo)致整個(gè)頁面卡在這個(gè)地方,其他任務(wù)無法執(zhí)行。

為了解決這個(gè)問題,Javascript語言將任務(wù)的執(zhí)行模式分成兩種:同步(Synchronous)和異步(Asynchronous).

"異步模式"非常重要。在瀏覽器端,耗時(shí)很長(zhǎng)的操作都應(yīng)該異步執(zhí)行,避免瀏覽器失去響應(yīng),***的例子就是Ajax操作。在服務(wù)器端,"異步模式"甚至是唯一的模式,因?yàn)閳?zhí)行環(huán)境是單線程的,如果允許同步執(zhí)行所有http請(qǐng)求,服務(wù)器性能會(huì)急劇下降,很快就會(huì)失去響應(yīng)。

setTimeout 函數(shù)的弊端

延時(shí)處理當(dāng)然少不了 setTimeout這個(gè)神器,很多人對(duì) setTimeout函數(shù)的理解就是:延時(shí)為 n 的話,函數(shù)會(huì)在 n 毫秒之后執(zhí)行。事實(shí)上并非如此,這里存在三個(gè)問題:

一個(gè)是 setTimeout函數(shù)的及時(shí)性問題, setTimeout是存在一定時(shí)間間隔的,并不是設(shè)定 n 毫秒執(zhí)行,他就是 n 毫秒執(zhí)行,可能會(huì)有一點(diǎn)時(shí)間的延遲,setInterval 和 setTimeout 函數(shù)運(yùn)轉(zhuǎn)的最短周期是 5ms 左右,這個(gè)數(shù)值在 HTML規(guī)范 中也是有提到的:

  • Let timeout be the second method argument, or zero if the argument was omitted.如果 timeout 參數(shù)沒有寫,默認(rèn)為 0
  • If nesting level is greater than 5, and timeout is less than 4, then increase timeout to 如果嵌套的層次大于 5 ,并且 timeout 設(shè)置的數(shù)值小于 4 則直接取 4.

其次是while循環(huán)會(huì)阻塞setTimeout的執(zhí)行

看這段代碼:

  1. var t = true
  2.  
  3. window.setTimeout(function (){ 
  4.     t = false
  5. },1000); 
  6.  
  7. while (t){} 
  8.  
  9. alert('end');  

結(jié)果是死循環(huán)導(dǎo)致setTimeout不執(zhí)行,也導(dǎo)致alert不執(zhí)行

js是單線程,所以會(huì)先執(zhí)行while(t){}再alert,但這個(gè)循環(huán)體是死循環(huán),所以永遠(yuǎn)不會(huì)執(zhí)行alert。

至于說為什么不執(zhí)行setTimeout,是因?yàn)閖s的工作機(jī)制是:當(dāng)線程中沒有執(zhí)行任何同步代碼的前提下才會(huì)執(zhí)行異步代碼,setTimeout是異步代碼,所以setTimeout只能等js空閑才會(huì)執(zhí)行,但死循環(huán)是永遠(yuǎn)不會(huì)空閑的,所以setTimeout也永遠(yuǎn)不會(huì)執(zhí)行。

第三是,try..catch捕捉不到他的錯(cuò)誤

異步編程方法

回調(diào)函數(shù)

這是異步編程最基本的方法。

假定有兩個(gè)函數(shù)f1和f2,后者等待前者的執(zhí)行結(jié)果。

  1. function f1(callback){ 
  2.   setTimeout(function () { 
  3.     // f1的任務(wù)代碼 
  4.     callback(); 
  5.   }, 1000); 
  6. f1(f2);  

采用這種方式,我們把同步操作變成了異步操作,f1不會(huì)堵塞程序運(yùn)行,相當(dāng)于先執(zhí)行程序的主要邏輯,將耗時(shí)的操作推遲執(zhí)行。

回調(diào)函數(shù)的優(yōu)點(diǎn)是簡(jiǎn)單、容易理解和部署,缺點(diǎn)是不利于代碼的閱讀和維護(hù),各個(gè)部分之間高度耦合(Coupling),流程會(huì)很混亂,而且每個(gè)任務(wù)只能指定一個(gè)回調(diào)函數(shù)。

事件監(jiān)聽

另一種思路是采用事件驅(qū)動(dòng)模式。任務(wù)的執(zhí)行不取決于代碼的順序,而取決于某個(gè)事件是否發(fā)生

  1. f1.on('done', f2); 
  2. function f1(){ 
  3.   setTimeout(function () { 
  4.     // f1的任務(wù)代碼 
  5.     f1.trigger('done'); 
  6.   }, 1000); 
  7.  

JS 和 瀏覽器提供的原生方法基本都是基于事件觸發(fā)機(jī)制的,耦合度很低,不過事件不能得到流程控制

Promises對(duì)象

Promises對(duì)象是CommonJS工作組提出的一種規(guī)范,目的是為異步編程提供統(tǒng)一接口。

Promises可以簡(jiǎn)單理解為一個(gè)事務(wù),這個(gè)事務(wù)存在三種狀態(tài):

  • 已經(jīng)完成了 resolved
  • 因?yàn)槟撤N原因被中斷了 rejected
  • 還在等待上一個(gè)事務(wù)結(jié)束 pending

簡(jiǎn)單說,它的思想是,每一個(gè)異步任務(wù)返回一個(gè)Promises對(duì)象,該對(duì)象有一個(gè)then方法,允許指定回調(diào)函數(shù),這樣寫的優(yōu)點(diǎn)在于,回調(diào)函數(shù)變成了鏈?zhǔn)綄懛?,程序的流程可以看得很清?/p>

Promises就是一個(gè)事務(wù)的管理器。他的作用就是將各種內(nèi)嵌回調(diào)的事務(wù)用流水形式表達(dá),其目的是為了簡(jiǎn)化編程,讓代碼邏輯更加清晰。

Promises可以分為:

  • 無錯(cuò)誤傳遞的 Promises,也就是事務(wù)不會(huì)因?yàn)槿魏卧蛑袛?,事?wù)隊(duì)列中的事項(xiàng)都會(huì)被依次處理,此過程中 Promises只有pending和 resolved兩種狀態(tài),沒有 rejected狀態(tài)。
  • 包含錯(cuò)誤的 Promises,每個(gè)事務(wù)的處理都必須使用容錯(cuò)機(jī)制來獲取結(jié)果,一旦出錯(cuò),就會(huì)將錯(cuò)誤信息傳遞給下一個(gè)事務(wù),如果錯(cuò)誤信息會(huì)影響下一個(gè)事務(wù),則下一個(gè)事務(wù)也會(huì) rejected,如果不會(huì),下一個(gè)事務(wù)可以正常執(zhí)行,依次類推。

此處留坑講generator實(shí)現(xiàn)異步編程

封裝好的實(shí)現(xiàn)

jquery的Deferred對(duì)象

簡(jiǎn)單說,Deferred對(duì)象就是jquery的回調(diào)函數(shù)解決方案。在英語中,defer的意思是"延遲",所以Deferred對(duì)象的含義就是"延遲"到未來某個(gè)點(diǎn)再執(zhí)行。

首先,回顧一下jquery的ajax操作的傳統(tǒng)寫法:

  1. $.ajax({ 
  2.     url: "test.html"
  3.     success: function(){ 
  4.       alert("哈哈,成功了!"); 
  5.     }, 
  6.     error:function(){ 
  7.       alert("出錯(cuò)啦!"); 
  8.     } 
  9.   });  

有了Deferred對(duì)象以后,寫法是這樣的:

  1. $.ajax("test.html"
  2.  .done(function(){ alert("哈哈,成功了!"); }) 
  3.  .fail(function(){ alert("出錯(cuò)啦!"); });  

可以看到,done()相當(dāng)于success方法,fail()相當(dāng)于error方法。采用鏈?zhǔn)綄懛ㄒ院?,代碼的可讀性大大提高。

了解jQuery.Deferred對(duì)象可以看下面這個(gè)表格。

 when.js

AngularJS內(nèi)置的Kris Kowal的Q框架,和cujoJS的when.js,兩者都是Promises/A規(guī)范的實(shí)現(xiàn)

when.js實(shí)例

  1. var getData = function() { 
  2.     var deferred = when.defer(); 
  3.  
  4.     $.getJSON(api, function(data){ 
  5.         deferred.resolve(data[0]); 
  6.     }); 
  7.  
  8.     return deferred.promise; 
  9.  
  10. var getImg = function(src) { 
  11.     var deferred = when.defer(); 
  12.  
  13.     var img = new Image(); 
  14.  
  15.     img.onload = function() { 
  16.         deferred.resolve(img); 
  17.     }; 
  18.  
  19.     img.src = src; 
  20.  
  21.     return deferred.promise; 
  22.  
  23. var showImg = function(img) { 
  24.     $(img).appendTo($('#container')); 
  25.  
  26. getData() 
  27. .then(getImg) 
  28. .then(showImg);  

看***三行代碼,是不是一目了然,非常的語義化

  1. var deferred = when.defer(); 

定義了一個(gè)deferred對(duì)象。

  1. deferred.resolve(data); 

在異步獲取數(shù)據(jù)完成時(shí),把數(shù)據(jù)作為參數(shù),調(diào)用deferred對(duì)象的resolve方法。

  1. return deferred.promise; 

返回了deferred對(duì)象的Promises屬性。

此處留坑講之前用過的step.js

擴(kuò)展閱讀

Javascript既是單線程又是異步的,請(qǐng)問這二者是否沖突,以及有什么區(qū)別?

Answer1:Javascript本身是單線程的,并沒有異步的特性。

由于 Javascript的運(yùn)用場(chǎng)景是瀏覽器,瀏覽器本身是典型的 GUI 工作線程,GUI 工作線程在絕大多數(shù)系統(tǒng)中都實(shí)現(xiàn)為事件處理,避免阻塞交互,因此產(chǎn)生了 Javascript異步基因。此后種種都源于此。

Answer2: JS的單線程是指一個(gè)瀏覽器進(jìn)程中只有一個(gè)JS的執(zhí)行線程,同一時(shí)刻內(nèi)只會(huì)有一段代碼在執(zhí)行(你可以使用IE的標(biāo)簽式瀏覽試試看效果,這時(shí)打開的多個(gè)頁面使用的都是同一個(gè)JS執(zhí)行線程,如果其中一個(gè)頁面在執(zhí)行一個(gè)運(yùn)算量較大的function時(shí),其他窗口的JS就會(huì)停止工作)。

而異步機(jī)制是瀏覽器的兩個(gè)或以上常駐線程共同完成的,例如異步請(qǐng)求是由兩個(gè)常駐線程:JS執(zhí)行線程和事件觸發(fā)線程共同完成的,JS的執(zhí)行線程發(fā)起異步請(qǐng)求(這時(shí)瀏覽器會(huì)開一條新的HTTP請(qǐng)求線程來執(zhí)行請(qǐng)求,這時(shí)JS的任務(wù)已完成,繼續(xù)執(zhí)行線程隊(duì)列中剩下的其他任務(wù)),然后在未來的某一時(shí)刻事件觸發(fā)線程監(jiān)視到之前的發(fā)起的HTTP請(qǐng)求已完成,它就會(huì)把完成事件插入到JS執(zhí)行隊(duì)列的尾部等待JS處理。又例如定時(shí)觸發(fā)(setTimeout和setinterval)是由瀏覽器的定時(shí)器線程執(zhí)行的定時(shí)計(jì)數(shù),然后在定時(shí)時(shí)間把定時(shí)處理函數(shù)的執(zhí)行請(qǐng)求插入到JS執(zhí)行隊(duì)列的尾端(所以用這兩個(gè)函數(shù)的時(shí)候,實(shí)際的執(zhí)行時(shí)間是大于或等于指定時(shí)間的,不保證能準(zhǔn)確定時(shí)的)。

所以,所謂的JS的單線程和異步更多的應(yīng)該是屬于瀏覽器的行為,他們之間沒有沖突,更不是同一種事物,沒有什么區(qū)別不區(qū)別的。

setTimeout(fn,0)立即執(zhí)行的問題

首先,不會(huì)立即執(zhí)行,原因:

setTimeout(fn,0)的作用很簡(jiǎn)單,就是為了把fn放到運(yùn)行隊(duì)列的***去執(zhí)行。也就是說,無論setTimeout(fn,0)寫在哪,都可以保證在隊(duì)列的***執(zhí)行。js解析器會(huì)把setTimeout(fn,0)里的fn壓到隊(duì)列的***,因?yàn)樗钱惒讲僮鳌S袀€(gè)延時(shí),具體是16ms還是4ms取決于瀏覽器

立即執(zhí)行還是有可能的,只要在你調(diào)用setTimeout的時(shí)候,滿足下面兩個(gè)條件:

  1. 剛好執(zhí)行到了當(dāng)前這一輪事件循環(huán)的底部。
  2. 剛好此時(shí)事件隊(duì)列為空。

那么setTimeout的回調(diào)函數(shù)就可以立即執(zhí)行。當(dāng)然“立即執(zhí)行”的意思是在任何其他代碼前執(zhí)行。

責(zé)任編輯:龐桂玉 來源: segmentfault
相關(guān)推薦

2020-10-15 13:29:57

javascript

2014-05-23 10:12:20

Javascript異步編程

2015-04-22 10:50:18

JavascriptJavascript異

2017-07-13 12:12:19

前端JavaScript異步編程

2021-06-02 09:01:19

JavaScript 前端異步編程

2011-11-11 15:47:22

JavaScript

2021-06-06 16:56:49

異步編程Completable

2021-12-10 07:47:30

Javascript異步編程

2011-11-10 10:23:56

Jscex

2021-06-06 19:51:07

JavaScript異步編程

2023-12-04 13:22:00

JavaScript異步編程

2011-07-27 14:10:43

javascript

2022-10-31 09:00:24

Promise數(shù)組參數(shù)

2013-01-07 10:44:00

JavaScriptjQueryJS

2016-10-21 11:04:07

JavaScript異步編程原理解析

2013-04-01 15:38:54

異步編程異步編程模型

2013-03-08 09:33:25

JavaScript同步異步

2012-03-31 11:04:32

ibmdw

2021-11-01 22:36:04

JavaScript

2021-06-28 08:10:59

JavaScript異步編程
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號(hào)