首頁 > web前端 > js教程 > 詳談javascript非同步程式設計_javascript技巧

詳談javascript非同步程式設計_javascript技巧

WBOY
發布: 2016-05-16 15:14:34
原創
1105 人瀏覽過

非同步程式設計帶來的問題在客戶端Javascript中並不明顯,但隨著伺服器端Javascript越來越廣的被使用,大量的非同步IO操作使得問題變得明顯。許多不同的方法都可以解決這個問題,本文討論了一些方法,但並不深入。大家需要依照自己的狀況選擇一個適於自己的方法。

本文為大家詳細介紹js中的非同步編程,具體內容如下

一 關於事件的非同步

事件是JavaScript中最重要的特徵,nodejs就是利用js這個非同步而設計出來的。所以這裡來講一下事件機制。

在一個js檔案中,如果要運行某一個函數,有2中手段,一個就是直接調用,比如foo(),第二就是利用事件來觸發,這中函數也叫回調函數,比如傳遞給setTimeout函數和onready屬性。

1.setTimeout函數中的事件非同步
setTimeout本質上也是一種非同步事件,當延遲時間到的時候觸發該事件,但是有的有的時候(其實也是大部分時候)都不會按照給定的延遲時間執行,先看下面的程式碼

  var start = new Date();
  setTimeout(function() {
   console.log('settimeout1:',new Date()-start);
  }, 500);
  while (new Date() - start < 1000) {
   console.log('in while');
  }
  document.getElementById('test').addEventListener('click', function(){
   console.log('test:',new Date()-start);
  }, false)
  for(var i=0;i<10000;i++){
   console.log('in for');
  }
  setTimeout(function(){
   console.log('settimeout2: ',new Date()-start);
  },1000);
  /* 10214
  in while
  index.jsp (第 19 行)
  10000
  in for
  index.jsp (第 25 行)
  settimeout1: 2263
  index.jsp (第 16 行)
  settimeout2: 3239
  index.jsp (第 28 行)
  test: 10006
  index.jsp (第 22 行)
  test: 28175
  index.jsp (第 22 行)
  test: 28791
  index.jsp (第 22 行)
  test: 28966
  index.jsp (第 22 行) */
登入後複製

如果按照正常的理解,延遲函數應該在500毫秒之後打斷while循環,而事實上並沒有,並且,我在while循環和for循環期間點擊div時候並沒有立即輸出test,給出的解釋就是:

a)事件隊列。呼叫setTimeout函數的時候,會把傳入它的回呼函數加入到事件佇列中去(事件已經初始化並且在記憶體了),然後繼續執行後面的程式碼,直到再也沒有程式碼可以運行(沒有正常的運行流了,不包括事件函數等非同步的內容),就會從事件佇列裡面pop出一個合適的事件來運行。

b)js是單執行緒的,事件處理器在執行緒空閒前是不會運作的。

2 普通事件的非同步和setTimeout類似
二 promise物件與deferred物件

1. promise
promise是一種解決ajax等非同步編程回調函數嵌套太多導致程式碼晦澀難懂的解決方案,特別是在nodejs中,非同步無處不在。不同的框架對promise的實現,一下是jquery中的promise的API。

這裡不講promise的實現原理,關於原理在另外的篇幅中介紹。

傳統的ajax非同步程式設計是這麼寫的(jquery1.5之前):

$.get('url', function(){
 $.get('url1', function(){
  $.get('url2', function(){

  }, 'json');
 }, 'json');
}, 'json');
登入後複製

 這麼寫程式碼為開發和維護帶來了極大的困難,好在jquery1.5以後引入了promise,就可以這麼寫了:

 $.ajax( "example.php" )
.done(function() { alert("success"); })
.fail(function() { alert("error"); })
.always(function() { alert("complete"); });
登入後複製

 現在看起來就明顯簡單多了。

2.deferred物件

var nanowrimoing = $.Deferred();
var wordGoal = 5000;
nanowrimoing.progress(function(wordCount) {
var percentComplete = Math.floor(wordCount / wordGoal * 100);
$('#indicator').text(percentComplete + '% complete');
});
nanowrimoing.done(function(){
$('#indicator').text('Good job!');
});
登入後複製

三.worker物件與多執行緒

四.非同步腳本載入

1.傳統腳本在頁面中的位置
腳本分為兩大類:阻塞式和非阻塞式。這裡的阻塞是指載入阻塞而不是運作阻塞。

<!DOCTYPE html>
<html>
<head>
<script src="headScript"></script>
<script defer src="deferredScript"></script>
</head>
<body>
 <script async defer src="chatWidget"></script>
 <script async defer src="asyncScript"></script>
</body>
</html>
登入後複製

上面這部分程式碼是比較標準的關於腳本在一個頁面中的位置,1.其中傳統的未加任何修飾的headScript是阻塞式的腳本,由於瀏覽器從上到下解釋執行JavaScript,所以這部分腳本檔案在一開始就會執行,在執行完之前是DOM是不會渲染的,但是head標籤裡面的css會載入。 2.有defer屬性的腳本會在DOM渲染的同時進行加載,但是會在DOM渲染完畢之後才開始執行,不幸的是,不是所有的瀏覽器都支援defer屬性,所以才會有了jquery(function)這個東西。 3.同時帶有async屬性和defer屬性時候,defer會覆蓋async,但是單獨有async的時候,腳本會在DOM渲染的時候載入並且運行。

2.可程式的腳本載入
如果不是一開始就在頁種引入js文件,而是透過使用者互動來實現動態的載入js腳本,可以透過程式設計方式加入。

瀏覽器取得伺服器腳本有2個方法,ajax取得並且透過eval函數執行,另外一個就是在DOM中插入

熱門教學
更多>
最新下載
更多>
網站特效
網站源碼
網站素材
前端模板