麻豆小视频在线观看_中文黄色一级片_久久久成人精品_成片免费观看视频大全_午夜精品久久久久久久99热浪潮_成人一区二区三区四区

首頁 > 編程 > JavaScript > 正文

深入理解JavaScript編程中的同步與異步機制

2019-11-20 12:10:21
字體:
來源:轉載
供稿:網友

 JavaScript的優勢之一是其如何處理異步代碼。異步代碼會被放入一個事件隊列,等到所有其他代碼執行后才進行,而不會阻塞線程。然而,對于初學者來說,書寫異步代碼可能會比較困難。而在這篇文章里,我將會消除你可能會有的任何困惑。
理解異步代碼

JavaScript最基礎的異步函數是setTimeout和setInterval。setTimeout會在一定時間后執行給定的函數。它接受一個回調函數作為第一參數和一個毫秒時間作為第二參數。以下是用法舉例:
 

console.log( "a" );setTimeout(function() {  console.log( "c" )}, 500 );setTimeout(function() {  console.log( "d" )}, 500 );setTimeout(function() {  console.log( "e" )}, 500 );console.log( "b" );

正如預期,控制臺先輸出“a”、“b”,大約500毫秒后,再看到“c”、“d”、“e”。我用“大約”是因為setTimeout事實上是不可預知的。實際上,甚至 HTML5規范都提到了這個問題:

  •     “這個API不能保證計時會如期準確地運行。由于CPU負載、其他任務等所導致的延遲是可以預料到的。”


有趣的是,直到在同一程序段中所有其余的代碼執行結束后,超時才會發生。所以如果設置了超時,同時執行了需長時間運行的函數,那么在該函數執行完成之前,超時甚至都不會啟動。實際上,異步函數,如setTimeout和setInterval,被壓入了稱之為Event Loop的隊列。

Event Loop是一個回調函數隊列。當異步函數執行時,回調函數會被壓入這個隊列。JavaScript引擎直到異步函數執行完成后,才會開始處理事件循環。這意味著JavaScript代碼不是多線程的,即使表現的行為相似。事件循環是一個先進先出(FIFO)隊列,這說明回調是按照它們被加入隊列的順序執行的。JavaScript被 node選做為開發語言,就是因為寫這樣的代碼多么簡單啊。

Ajax

異步Javascript與XML(AJAX)永久性的改變了Javascript語言的狀況。突然間,瀏覽器不再需要重新加載即可更新web頁面。 在不同的瀏覽器中實現Ajax的代碼可能漫長并且乏味;但是,幸虧有jQuery(還有其他庫)的幫助,我們能夠以很容易并且優雅的方式實現客戶端-服務器端通訊。

我們可以使用jQuery跨瀏覽器接口$.ajax很容易地檢索數據,然而卻不能呈現幕后發生了什么。比如:

var data;$.ajax({  url: "some/url/1",  success: function( data ) {    // But, this will!    console.log( data );  }})// Oops, this won't work...console.log( data );

較容易犯的錯誤,是在調用$.ajax之后馬上使用data,但是實際上是這樣的:
 

xmlhttp.open( "GET", "some/ur/1", true );xmlhttp.onreadystatechange = function( data ) {  if ( xmlhttp.readyState === 4 ) {    console.log( data );  }};xmlhttp.send( null );

底層的XmlHttpRequest對象發起請求,設置回調函數用來處理XHR的readystatechnage事件。然后執行XHR的send方法。在XHR運行中,當其屬性readyState改變時readystatechange事件就會被觸發,只有在XHR從遠端服務器接收響應結束時回調函數才會觸發執行。

處理異步代碼

異步編程很容易陷入我們常說的“回調地獄”。因為事實上幾乎JS中的所有異步函數都用到了回調,連續執行幾個異步函數的結果就是層層嵌套的回調函數以及隨之而來的復雜代碼。

node.js中的許多函數也是異步的。因此如下的代碼基本上很常見:
 

var fs = require( "fs" );fs.exists( "index.js", function() {  fs.readFile( "index.js", "utf8", function( err, contents ) {    contents = someFunction( contents ); // do something with contents    fs.writeFile( "index.js", "utf8", function() {      console.log( "whew! Done finally..." );    });  });});console.log( "executing..." );

下面的客戶端代碼也很多見:

 

GMaps.geocode({  address: fromAddress,  callback: function( results, status ) {    if ( status == "OK" ) {      fromLatLng = results[0].geometry.location;      GMaps.geocode({        address: toAddress,        callback: function( results, status ) {          if ( status == "OK" ) {            toLatLng = results[0].geometry.location;            map.getRoutes({              origin: [ fromLatLng.lat(), fromLatLng.lng() ],              destination: [ toLatLng.lat(), toLatLng.lng() ],              travelMode: "driving",              unitSystem: "imperial",              callback: function( e ){                console.log( "ANNNND FINALLY here's the directions..." );                // do something with e              }            });          }        }      });    }  }});

Nested callbacks can get really nasty, but there are several solutions to this style of coding.

嵌套的回調很容易帶來代碼中的“壞味道”,不過你可以用以下的幾種風格來嘗試解決這個問題

  •     The problem isn't with the language itself; it's with the way programmers use the language ― Async Javascript.

    沒有糟糕的語言,只有糟糕的程序猿 ――異步JavaSript


命名函數

清除嵌套回調的一個便捷的解決方案是簡單的避免雙層以上的嵌套。傳遞一個命名函數給作為回調參數,而不是傳遞匿名函數:
 

var fromLatLng, toLatLng;var routeDone = function( e ){  console.log( "ANNNND FINALLY here's the directions..." );  // do something with e};var toAddressDone = function( results, status ) {  if ( status == "OK" ) {    toLatLng = results[0].geometry.location;    map.getRoutes({      origin: [ fromLatLng.lat(), fromLatLng.lng() ],      destination: [ toLatLng.lat(), toLatLng.lng() ],      travelMode: "driving",      unitSystem: "imperial",      callback: routeDone    });  }};var fromAddressDone = function( results, status ) {  if ( status == "OK" ) {    fromLatLng = results[0].geometry.location;    GMaps.geocode({      address: toAddress,      callback: toAddressDone    });  }};GMaps.geocode({  address: fromAddress,  callback: fromAddressDone});

此外, async.js 庫可以幫助我們處理多重Ajax requests/responses. 例如:
 

async.parallel([  function( done ) {    GMaps.geocode({      address: toAddress,      callback: function( result ) {        done( null, result );      }    });  },  function( done ) {    GMaps.geocode({      address: fromAddress,      callback: function( result ) {        done( null, result );      }    });  }], function( errors, results ) {  getRoute( results[0], results[1] );});

這段代碼執行兩個異步函數,每個函數都接收一個名為"done"的回調函數并在函數結束的時候調用它。當兩個"done"回調函數結束后,parallel函數的回調函數被調用并執行或處理這兩個異步函數產生的結果或錯誤。

Promises模型
引自 CommonJS/A:

  •     promise表示一個操作獨立完成后返回的最終結果。

有很多庫都包含了promise模型,其中jQuery已經有了一個可使用且很出色的promise API。jQuery在1.5版本引入了Deferred對象,并可以在返回promise的函數中使用jQuery.Deferred的構造結果。而返回promise的函數則用于執行某種異步操作并解決完成后的延遲。
 

var geocode = function( address ) {  var dfd = new $.Deferred();  GMaps.geocode({    address: address,    callback: function( response, status ) {      return dfd.resolve( response );    }  });  return dfd.promise();};var getRoute = function( fromLatLng, toLatLng ) {  var dfd = new $.Deferred();  map.getRoutes({    origin: [ fromLatLng.lat(), fromLatLng.lng() ],    destination: [ toLatLng.lat(), toLatLng.lng() ],    travelMode: "driving",    unitSystem: "imperial",    callback: function( e ) {      return dfd.resolve( e );    }  });  return dfd.promise();};var doSomethingCoolWithDirections = function( route ) {  // do something with route};$.when( geocode( fromAddress ), geocode( toAddress ) ).  then(function( fromLatLng, toLatLng ) {    getRoute( fromLatLng, toLatLng ).then( doSomethingCoolWithDirections );  });

這允許你執行兩個異步函數后,等待它們的結果,之后再用先前兩個調用的結果來執行另外一個函數。

  •     promise表示一個操作獨立完成后返回的最終結果。

在這段代碼里,geocode方法執行了兩次并返回了一個promise。異步函數之后執行,并在其回調里調用了resolve。然后,一旦兩次調用resolve完成,then將會執行,其接收了之前兩次調用geocode的返回結果。結果之后被傳入getRoute,此方法也返回一個promise。最終,當getRoute的promise解決后,doSomethingCoolWithDirections回調就執行了。
 
事件
事件是另一種當異步回調完成處理后的通訊方式。一個對象可以成為發射器并派發事件,而另外的對象則監聽這些事件。這種類型的事件處理方式稱之為 觀察者模式 。 backbone.js 庫在withBackbone.Events中就創建了這樣的功能模塊。
 

var SomeModel = Backbone.Model.extend({  url: "/someurl"});var SomeView = Backbone.View.extend({  initialize: function() {    this.model.on( "reset", this.render, this );    this.model.fetch();  },  render: function( data ) {    // do something with data  }});var view = new SomeView({  model: new SomeModel()});

還有其他用于發射事件的混合例子和函數庫,例如 jQuery Event Emitter , EventEmitter , monologue.js ,以及node.js內建的 EventEmitter 模塊。

  •     事件循環是一個回調函數的隊列。

一個類似的派發消息的方式稱為 中介者模式 , postal.js 庫中用的即是這種方式。在中介者模式,有一個用于所有對象監聽和派發事件的中間人。在這種模式下,一個對象不與另外的對象產生直接聯系,從而使得對象間都互相分離。

絕不要返回promise到一個公用的API。這不僅關系到了API用戶對promises的使用,也使得重構更加困難。不過,內部用途的promises和外部接口的事件的結合,卻可以讓應用更低耦合且便于測試。

在先前的例子里面,doSomethingCoolWithDirections回調函數在兩個geocode函數完成后執行。然后,doSomethingCoolWithDirections才會獲得從getRoute接收到的響應,再將其作為消息發送出去。
 

var doSomethingCoolWithDirections = function( route ) {  postal.channel( "ui" ).publish( "directions.done", {    route: route  });};

這允許了應用的其他部分不需要直接引用產生請求的對象,就可以響應異步回調。而在取得命令時,很可能頁面的好多區域都需要更新。在一個典型的jQuery Ajax過程中,當接收到的命令變化時,要順利的回調可能就得做相應的調整了。這可能會使得代碼難以維護,但通過使用消息,處理UI多個區域的更新就會簡單得多了。
 

var UI = function() {  this.channel = postal.channel( "ui" );  this.channel.subscribe( "directions.done", this.updateDirections ).withContext( this );};UI.prototype.updateDirections = function( data ) {  // The route is available on data.route, now just update the UI};app.ui = new UI();

另外一些基于中介者模式傳送消息的庫有 amplify, PubSubJS, and radio.js。

結論

JavaScript 使得編寫異步代碼很容易. 使用 promises, 事件, 或者命名函數來避免“callback hell”. 為獲取更多javascript異步編程信息,請點擊Async JavaScript: Build More Responsive Apps with Less . 更多的實例托管在github上,地址NetTutsAsyncJS,趕快Clone吧 !

發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表
主站蜘蛛池模板: 一级电影免费看 | 久久一本日日摸夜夜添 | 91在线精品亚洲一区二区 | www.99tv| 日韩一级成人 | www.理论片| 91成人精品 | 娇喘在线 | 黄色一级片在线观看 | 色综合视频 | 一区国产在线观看 | 久久久久久久久久美女 | 成人网在线观看 | 国产一级一片免费播放 | 欧美精品一区二区三区在线 | 一区二区三区日韩精品 | 久草在线综合 | 在线免费小视频 | 久久精品久久久久 | 另类亚洲孕妇分娩网址 | 欧美毛片免费观看 | 深夜免费福利视频 | 国产精品欧美久久久久一区二区 | 婷婷一区二区三区 | 色啪综合 | 国产一级爱c视频 | 久久久一区二区精品 | 亚洲一区在线免费视频 | 久久久久久艹 | 超碰一区 | 久久精品欧美电影 | 免费看毛片的网站 | 久久久成人动漫 | 久久精品国产久精国产 | 激情综合网俺也去 | 91女上位 在线播放 性欧美日本 | 亚洲第一页在线观看 | 国外成人在线视频 | 久久久久国产成人精品亚洲午夜 | av国产免费 | 色综合久久久久久久久久久 |