最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

理解javascript異步編程

 更新時間:2016年01月27日 11:38:14   作者:sundway  
這篇文章主要為大家介紹了javascript異步編程,從淺入深的學習javascript異步編程,對javascript異步編程感興趣的小伙伴們可以參考一下

一、異步機制

JavaScript的執(zhí)行環(huán)境是單線程的,單線程的好處是執(zhí)行環(huán)境簡單,不用去考慮諸如資源同步,死鎖等多線程阻塞式編程等所需要面對的惱人的問題。但帶來的壞處是當一個任務執(zhí)行時間較長時,后面的任務會等待很長時間。在瀏覽器端就會出現瀏覽器假死,鼠標無法響應等情況。所以在瀏覽器端,耗時很長的操作都應該異步執(zhí)行,避免瀏覽器失去響應。所謂異步執(zhí)行,不同于同步執(zhí)行(程序的執(zhí)行順序與任務的排列順序是一致的、同步的),每一個任務有一個或多個回調函數(callback),前一個任務結束后,不是執(zhí)行后一個任務,而是執(zhí)行回調函數,后一個任務則是不等前一個任務結束就執(zhí)行,所以程序的執(zhí)行順序與任務的排列順序是不一致的、異步的。既然Javascript是單線程的,那它又如何能夠異步的執(zhí)行呢?

二、Javascript線程模型和事件驅動

JavaScript有一個基于事件循環(huán)的并發(fā)模式。這個模式與C語言和java有很大不同。

運行時的概念


函數調用形成堆棧幀。

function f(b){
  var a = 12;
  return a+b+35;
}

function g(x){
  var m = 4;
  return f(m*x);
}

g(21);

當調用函數g時,創(chuàng)建第一個包含g參數和局部變量的幀。當g函數調用f函數時,創(chuàng)建包含f參數和局部變量第二個堆棧幀并推到第一個堆棧幀的頂部。當f返回時,頂部的堆棧幀元素被彈出(只留下g調用)。當g函數返回時,堆棧為空。


堆是一個大型的非結構化區(qū)域,對象被分配到堆中。
隊列
一個javascript運行環(huán)境包含一個信息隊列,這個隊列是一系列將被執(zhí)行的信息列表。每一個消息被關聯(lián)到一個函數上。當堆棧為空時,從消息隊列中取出一個消息并進行處理。該處理包含調用相關的函數(以及因此產生一個初始化的堆棧幀)。當堆棧再次為空時,消息處理結束。
事件循環(huán)

事件循環(huán)的名字源于它的實現,經常像下面這樣:

while(queue.waitForMessage()){
 queue.processNextMessage();
}

queue.waitForMessage同步等待一個消息。

1、運行到完成
每個消息完全處理之后,其它消息才會被處理。這樣的好處就是當一個函數不能被提前,只能等其他函數執(zhí)行完畢(并且可以修改數據的函數操作)。這不同于C,例如,如果一個函數在一個線程運行時,它可以停在任何點運行在另一個線程一些其他的代碼。這種模式的缺點是,如果一個消息時間過長完成,Web應用程序無法處理像點擊或滾動的用戶交互。該瀏覽器可緩解此與“腳本花費的時間太長運行”對話框。一個很好的做法,遵循的是使信息處理短,如果可能削減一個消息到幾條消息。
2、添加消息
在網頁瀏覽器中,事件可以在任何時候添加,一個事件發(fā)生并伴隨事件監(jiān)聽綁定到事件上。如果沒有事件監(jiān)聽,則事件丟失。就像點擊一個元素,元素上綁定點擊事件。調用setTimeout時,當函數的第二個參數時間被傳遞進去,將添加一個消息到隊列中。如果在隊列中沒有其他消息,該消息被立即處理;然而,如果有消息,則setTimeout的信息將必須等待其它消息以進行處理。由于這個原因,第二個參數是最小的時間,而不是一個保證時間。
3、幾個運行環(huán)境之間的通信
一個web worker或跨域iframe都有自己的堆棧,堆,和消息隊列。兩個不同的運行環(huán)境只能通過postMessage的方法發(fā)送消息進行通信。這種方法增加了一個消息到其他運行時,如果后者監(jiān)聽消息事件。
從不阻塞

事件循環(huán)模型是javascript的一個很有意思的屬性,不像其它語言,它從不阻塞。假定瀏覽器中有一個專門用于事件調度的實例(該實例可以是一個線程,我們可以稱之為事件分發(fā)線程event dispatch thread),該實例的工作就是一個不結束的循環(huán),從事件隊列中取出事件,處理所有很事件關聯(lián)的回調函數(event handler)。注意回調函數是在Javascript的主線程中運行的,而非事件分發(fā)線程中,以保證事件處理不會發(fā)生阻塞。通過事件和回調的I/O操作是一個典型的表現,所以當應用等待索引型數據庫查詢返回或XHR請求返回時,它仍然可以處理其他事情比如用戶輸入。

三、回調

回調是javascript的基礎,函數被作為參數進行傳遞。像下面:

f1();
f2();
f3();

如果f1中執(zhí)行了大量的耗時操作,而且f2需要在f1之后執(zhí)行。則程序可以改為回調的形式。如下:

function f1(callback){
  setTimeout(function () {
    // f1的大量耗時任務代碼并的到三個結果i,l,you.
    console.log("this is function1");
    var i = "i", l = "love", y = "you";
    if (callback && typeof(callback) === "function") {
      callback(i,l,y);
    }
  }, 50);
}

function f2(a, b, c) {
  alert(a + " " + b + " " + c);
  console.log("this is function2");
}

function f3(){console.log("this is function3");}
f1(f2);
f3();

運行結果:

this is function3
this is function1
i love you
this is function2

采用這種方式,我們把同步操作變成了異步操作,f1不會堵塞程序運行,相當于先執(zhí)行程序的主要邏輯,將耗時的操作推遲執(zhí)行。
回調函數的優(yōu)點是簡單,輕量級(不需要額外的庫)。缺點是各個部分之間高度耦合(Coupling),流程會很混亂,而且每個任務只能指定一個回調函數。某個操作需要經過多個非阻塞的IO操作,每一個結果都是通過回調,產生意大利面條式(spaghetti)的代碼。

operation1(function(err, result) {
  operation2(function(err, result) {
    operation3(function(err, result) {
      operation4(function(err, result) {
        operation5(function(err, result) {
          // do something useful
        })
      })
    })
  })
})

四、事件監(jiān)聽

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

// plain, non-jQuery version of hooking up an event handler
var clickity = document.getElementById("clickity");
clickity.addEventListener("click", function (e) {
  //console log, since it's like ALL real world scenarios, amirite?
  console.log("Alas, someone is pressing my buttons…");
});

// the obligatory jQuery version
$("#clickity").on("click", function (e) {
  console.log("Alas, someone is pressing my buttons…");
});

也可以自定義事件進行監(jiān)聽,關于自定義事件,屬于另外一部分的內容。這種方法的優(yōu)點是比較容易理解,可以綁定多個事件,每個事件可以指定多個回調函數,而且可以"去耦合"(Decoupling),有利于實現模塊化。缺點是整個程序都要變成事件驅動型,運行流程會變得很不清晰。

五、觀察者模式

我們假定,存在一個"信號中心",某個任務執(zhí)行完成,就向信號中心"發(fā)布"(publish)一個信號,其他任務可以向信號中心"訂閱"(subscribe)這個信號,從而知道什么時候自己可以開始執(zhí)行。這就叫做"發(fā)布/訂閱模式"(publish-subscribe pattern),又稱"觀察者模式"(observer pattern)。

var pubsub = (function(){
  var q = {}
    topics = {},
    subUid = -1;
  //發(fā)布消息
  q.publish = function(topic, args) {
    if(!topics[topic]) {return;}
    var subs = topics[topic],
      len = subs.length;
    while(len--) {
      subs[len].func(topic, args);
    }
    return this;
  };
  //訂閱事件
  q.subscribe = function(topic, func) {
    topics[topic] = topics[topic] ? topics[topic] : [];
    var token = (++subUid).toString();
    topics[topic].push({
      token : token,
      func : func
    });
    return token;
  };
  return q;
  //取消訂閱就不寫了,遍歷topics,然后通過保存前面返回token,刪除指定元素
})();
//觸發(fā)的事件
var f2 = function(topics, data) {
  console.log("logging:" + topics + ":" + data);
  console.log("this is function2");
}

function f1(){
   setTimeout(function () {
      // f1的任務代碼
      console.log("this is function1");
    //發(fā)布消息'done'
    pubsub .publish('done', 'hello world');
    }, 1000);
}
pubsub.subscribe('done', f2);
f1();

上面代碼的運行結果為:

this is function1
logging:done:hello world
this is function2

觀察者模式的實現方法有很多種,也可以直接借用第三方庫。這種方法的性質與"事件監(jiān)聽"類似(觀察者模式和自定義事件非常相似),但是明顯優(yōu)于后者。觀察者模式和事件監(jiān)聽一樣具有良好的去耦性,并且有一個消息中心,通過對消息中心的處理,可以良好地監(jiān)控程序運行。

六、Promises對象

Promises的概念是由CommonJS小組的成員在 Promises/A規(guī)范 中提出來的。Promises被逐漸用作一種管理異步操作回調的方法,但出于它們的設計,它們遠比那個有用得多。Promise允許我們以同步的方式寫代碼,同時給予我們代碼的異步執(zhí)行。

function f1(){
  var def = $.Deferred();
  setTimeout(function () {
    // f1的任務代碼
    console.log("this is f1");
    def.resolve(); 
  }, 500);
  return def.promise();
}

function f2(){
  console.log("this is f2");
}

f1().then(f2);

上面代碼的運行結果為:

this is f1
this is f2

上面引用的是jquery對Promises/A的實現,jquery中還有一系列方法,具體可參考:Deferred Object.關于Promises,強烈建議讀一下You're Missing the Point of Promises.還有很多第三方庫實現了Promises,如:Q、Bluebird、 mmDeferred 等。Promise(中文:承諾)其實為一個有限狀態(tài)機,共有三種狀態(tài):pending(執(zhí)行中)、fulfilled(執(zhí)行成功)和rejected(執(zhí)行失?。?。其中pending為初始狀態(tài),fulfilled和rejected為結束狀態(tài)(結束狀態(tài)表示promise的生命周期已結束)。狀態(tài)轉換關系為:pending->fulfilled,pending->rejected。隨著狀態(tài)的轉換將觸發(fā)各種事件(如執(zhí)行成功事件、執(zhí)行失敗事件等)。 下節(jié)具體講述狀態(tài)機實現js異步編程。

七、狀態(tài)機

Promises的本質實際就是通過狀態(tài)機來實現的,把異步操作與對象的狀態(tài)改變掛鉤,當異步操作結束的時候,發(fā)生相應的狀態(tài)改變,由此再觸發(fā)其他操作。這要比回調函數、事件監(jiān)聽、發(fā)布/訂閱等解決方案,在邏輯上更合理,更易于降低代碼的復雜度。關于Promises可參考:JS魔法堂:剖析源碼理解Promises/A規(guī)范 。

八、ES6對異步的支持

這是一個新的技術,成為2015年的ECMAScript(ES6)標準的一部分。該技術的規(guī)范已經完成,但實施情況在不同的瀏覽器不同,在瀏覽器中的支持情況如下。

var f1 = new Promise(function(resolve, reject) {
  setTimeout(function () {
    // f1的任務代碼
    console.log("this is f1");
    resolve("Success");

  }, 500); 
});
function f2(val){
  console.log(val + ":" + "this is f2");
}
function f3(){
  console.log("this is f3")
}
f1.then(f2);
f3();

以上代碼在Chrome 版本43中的運行結果為:

this is f3
this is f1
Success:this is f2

以上就是針對javascript異步編程的了解學習,之后還有相關文章進行分享,不要錯過哦。

相關文章

  • Js保留小數點的4種效果實現代碼分享

    Js保留小數點的4種效果實現代碼分享

    jvascript 計算保留小數點一兩位,有四種不同效果,非常適用于商城類網站,需要的朋友可以參考下
    2014-04-04
  • JavaScript針對SPA應用的前端優(yōu)化策略概述

    JavaScript針對SPA應用的前端優(yōu)化策略概述

    前端性能優(yōu)化是Web開發(fā)中非常重要的一環(huán),優(yōu)化能夠有效地提高網站的加載速度和用戶體驗,而對于SPA來說,由于其特殊的加載方式和數據交互方式,其優(yōu)化策略也具有一定的特殊性,需要詳細了解可以參考下文
    2023-05-05
  • JS庫particles.js創(chuàng)建超炫背景粒子插件(附源碼下載)

    JS庫particles.js創(chuàng)建超炫背景粒子插件(附源碼下載)

    particles.js用于創(chuàng)建粒子的輕量級 JavaScript 庫。使用方法非常簡單,代碼也很容易實現,下面通過本文給大家分享JS庫particles.js創(chuàng)建超炫背景粒子插件附源碼下載,需要的朋友參考下吧
    2017-09-09
  • Promise面試題詳解之控制并發(fā)

    Promise面試題詳解之控制并發(fā)

    promise面試中經常會遇到的一個問題就是關于控制并發(fā)的,所以下面這篇文章主要給大家介紹了關于Promise面試題之控制并發(fā)的相關資料,需要的朋友可以參考下
    2021-05-05
  • JS+CSS實現的拖動分頁效果實例

    JS+CSS實現的拖動分頁效果實例

    這篇文章主要介紹了JS+CSS實現的拖動分頁效果,可實現鼠標拖動頁面翻轉到上一頁或下一頁的功能,涉及javascript操作頁面元素與css樣式的相關技巧,需要的朋友可以參考下
    2015-05-05
  • 用JS實現飛機大戰(zhàn)小游戲

    用JS實現飛機大戰(zhàn)小游戲

    這篇文章主要為大家詳細介紹了用JS實現飛機大戰(zhàn)小游戲,文中示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2021-06-06
  • JS動態(tài)修改表格cellPadding和cellSpacing的方法

    JS動態(tài)修改表格cellPadding和cellSpacing的方法

    這篇文章主要介紹了JS動態(tài)修改表格cellPadding和cellSpacing的方法,涉及javascript操作cellPadding和cellSpacing屬性的技巧,具有一定參考借鑒價值,需要的朋友可以參考下
    2015-03-03
  • JS封裝通過className獲取元素的函數示例

    JS封裝通過className獲取元素的函數示例

    這篇文章主要介紹了JS封裝通過className獲取元素的函數,涉及javascript針對頁面元素屬性的遍歷與數組相關操作技巧,需要的朋友可以參考下
    2016-12-12
  • JavaScript利用事件循環(huán)實現數據預加載

    JavaScript利用事件循環(huán)實現數據預加載

    當我們請求好第一頁數據后,將請求下一頁放到異步任務隊列里(宏任務或者微任務),等當前同步任務執(zhí)行完成后,立馬請求下一頁數據,本文給大家介紹了JavaScript如何利用事件循環(huán)實現數據預加載,需要的朋友可以參考下
    2024-05-05
  • 用jscript實現新建word文檔

    用jscript實現新建word文檔

    用jscript實現新建word文檔...
    2007-06-06

最新評論

康定县| 菏泽市| 隆尧县| 丰宁| 皋兰县| 临海市| 临颍县| 景宁| 伊金霍洛旗| 弥渡县| 门头沟区| 阿尔山市| 延庆县| 温泉县| 遵义市| 东宁县| 霸州市| 皋兰县| 会宁县| 新乡市| 玉环县| 洛扎县| 温泉县| 富阳市| 平湖市| 财经| 南阳市| 临城县| 临朐县| 贵南县| 新兴县| 固安县| 桐柏县| 丘北县| 招远市| 平远县| 微博| 洮南市| 双峰县| 若尔盖县| 浦江县|