標籤:非同步任務 publish font 邏輯 ted pos 請求 span 訊號
非同步編程:
在瀏覽器端,非同步編程非常重要,耗時很長的操作都應該非同步執行,避免瀏覽器失去響應。最常見的例子就是通過AJAX向伺服器發送非同步請求。
非同步編程有很多種方法
1、回呼函數
比如有兩個函數
f1();
f2();//f2依賴於f1的執行狀態
如果f1耗時很長,它會阻塞後面程式的運行
我們利用setTimeout來改寫f1,因為setTimeout是非同步
function f1(callback){ setTimeout(function(){ //f1的代碼,耗時很長,這裡是又開啟了一個線程, callback();//f1完後執行callback() },1000)}f1(f2);//這段代碼不會阻塞後面的程式運行,f2依賴於f1的執行狀態
採用這種方式,我們把同步操作變成了非同步作業,f1不會堵塞程式運行,相當於先執行程式的主要邏輯,將耗時的操作延遲執行。
這種方法很簡單,也容易理解,但是不利於代碼的閱讀和維護,各個部分之間高度耦合,而且每個任務只能指定一個回呼函數
2、事件監聽
另一種思路是採用事件驅動模式。任務的執行不取決於代碼的順序,而取決於某個事件是否發生。
f1.on(‘done‘,f2);//為f1綁定一個事件(f2訂閱事件)function f1(){ setTimeout(function(){ //f1的代碼 f1.trigger(‘done‘);//f1觸發這個事件(f1發布事件) },1000)}
這種方法可以綁定多個事件,每個事件可以指定多個回呼函數,而且可以"去耦合"有利於實現模組化。
缺點是整個程式都要變成事件驅動型,運行流程會變得很不清晰。
3.發布訂閱模式
又稱觀察者模式,它廣泛的應用於非同步編程中,是一種代替回呼函數的方案。
上面講到的事件,可以理解成“訊號”
假定,存在一個"訊號中心",某個任務執行完成,就向訊號中心"發布"一個訊號,其他任務可以向訊號中心"訂閱"這個訊號,從而知道什麼時候自己可以開始執行。
這就叫做"發布/訂閱模式",又稱"觀察者模式"
發布訂閱者模式可以去除對象之間的耦合,有利於實現模組化,而且我們可以通過查看"訊息中心",瞭解存在多少訊號、每個訊號有多少訂閱者,從而監控程式的運行。
這個模式有多種實現,下面採用的是jQuery的一個外掛程式。
//f2向"訊號中心"jQuery訂閱"done"訊號jQuery.subscribe("done", f2);function f1(){ setTimeout(function () { // f1的任務代碼 jQuery.publish("done");//f1執行完成後,向"訊號中心"jQuery發布"done"訊號,從而引發f2的執行。 }, 1000); }
4.Promise對象
Promises對象是CommonJS工作群組提出的一種規範,目的是為非同步編程提供統一介面,使得控制非同步作業更加的容易
ES6將Promsie納入了規範,並原生提供了Promise對象
簡單說,它的思想是,每一個非同步任務返回一個Promise對象,該對象有一個then方法,允許指定回呼函數。比如,f1的回呼函數f2,可以寫成:
f1().then(f2);
promise優點在於避免層層嵌套的回呼函數,回呼函數變成了鏈式寫法,程式的流程可以看得很清楚,並且提供了統一的介面,使得控制非同步作業更加的容易。
而且,它還有一個前面三種方法都沒有的好處:如果一個任務已經完成,再添加回呼函數,該回呼函數會立即執行。所以,你不用擔心是否錯過了某個事件或訊號。
這種方法的缺點就是編寫和理解,都相對比較難。
例子:我們用promise對象實現AJAX操作
var getJSON=function(url){ var promise=new Promise(function(resolve,reject){ //AJAX操作 $.ajax({ url:url, type:‘GET‘, dataType:‘json‘, success:function(data,status,xhr){ resolve(data); }, error:function(xhr,textStatus){ reject(textStatus); } }) }) return promise;}
getJSON(‘/post.json‘) //任務Resolve狀態的回呼函數,返回是這個promise對象 .then(function(value) { console.log(value); }) //任務Rejected狀態的回呼函數 .catch(function(error){ console.log(error); })
採用鏈式的then方法可以指定按照次序調用的回呼函數
如果前一個回呼函數返回的是另外一個promise對象,那麼後一個回呼函數就會等待該promise對象狀態發生變化再調用
getJSON("url1") .then(function(value){ return getJSON(value.url2);//返回另外一個promise對象 }) //另外一個promise對象Resolve狀態的回呼函數 .then(function(value){ console.log(value) })
JavaScript非同步編程的方法