成人国产在线小视频_日韩寡妇人妻调教在线播放_色成人www永久在线观看_2018国产精品久久_亚洲欧美高清在线30p_亚洲少妇综合一区_黄色在线播放国产_亚洲另类技巧小说校园_国产主播xx日韩_a级毛片在线免费

資訊專欄INFORMATION COLUMN

javascript異步編程詳解

huangjinnan / 967人閱讀

摘要:在服務(wù)器端,異步模式甚至是唯一的模式,因為執(zhí)行環(huán)境是單線程的,如果允許同步執(zhí)行所有請求,服務(wù)器性能會急劇下降,很快就會失去響應(yīng)。第三是,捕捉不到他的錯誤異步編程方法回調(diào)函數(shù)這是異步編程最基本的方法。

前言

你可能知道,Javascript語言的執(zhí)行環(huán)境是"單線程"(single thread)。
所謂"單線程",就是指一次只能完成一件任務(wù)。如果有多個任務(wù),就必須排隊,前面一個任務(wù)完成,再執(zhí)行后面一個任務(wù),以此類推。
這種模式的好處是實現(xiàn)起來比較簡單,執(zhí)行環(huán)境相對單純;壞處是只要有一個任務(wù)耗時很長,后面的任務(wù)都必須排隊等著,會拖延整個程序的執(zhí)行。常見的瀏覽器無響應(yīng)(假死),往往就是因為某一段Javascript代碼長時間運行(比如死循環(huán)),導致整個頁面卡在這個地方,其他任務(wù)無法執(zhí)行。
為了解決這個問題,Javascript語言將任務(wù)的執(zhí)行模式分成兩種:同步(Synchronous)和異步(Asynchronous).
"異步模式"非常重要。在瀏覽器端,耗時很長的操作都應(yīng)該異步執(zhí)行,避免瀏覽器失去響應(yīng),最好的例子就是Ajax操作。在服務(wù)器端,"異步模式"甚至是唯一的模式,因為執(zhí)行環(huán)境是單線程的,如果允許同步執(zhí)行所有http請求,服務(wù)器性能會急劇下降,很快就會失去響應(yīng)。

setTimeout 函數(shù)的弊端

延時處理當然少不了 setTimeout這個神器,很多人對 setTimeout函數(shù)的理解就是:延時為 n 的話,函數(shù)會在 n 毫秒之后執(zhí)行。事實上并非如此,這里存在三個問題:
一個是 setTimeout函數(shù)的及時性問題, setTimeout是存在一定時間間隔的,并不是設(shè)定 n 毫秒執(zhí)行,他就是 n 毫秒執(zhí)行,可能會有一點時間的延遲,setIntervalsetTimeout 函數(shù)運轉(zhuǎn)的最短周期是 5ms 左右,這個數(shù)值在 HTML規(guī)范 中也是有提到的:

Let timeout be the second method argument, or zero if the argument was omitted.
如果 timeout 參數(shù)沒有寫,默認為 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)會阻塞setTimeout的執(zhí)行

看這段代碼:
var t = true;

window.setTimeout(function (){
    t = false;
},1000);

while (t){}

alert("end");

結(jié)果是死循環(huán)導致setTimeout不執(zhí)行,也導致alert不執(zhí)行
js是單線程,所以會先執(zhí)行while(t){}alert,但這個循環(huán)體是死循環(huán),所以永遠不會執(zhí)行alert。
至于說為什么不執(zhí)行setTimeout,是因為js的工作機制是:當線程中沒有執(zhí)行任何同步代碼的前提下才會執(zhí)行異步代碼,setTimeout是異步代碼,所以setTimeout只能等js空閑才會執(zhí)行,但死循環(huán)是永遠不會空閑的,所以setTimeout也永遠不會執(zhí)行。

第三是,try..catch捕捉不到他的錯誤

異步編程方法 回調(diào)函數(shù)

這是異步編程最基本的方法。
假定有兩個函數(shù)f1和f2,后者等待前者的執(zhí)行結(jié)果。

function f1(callback){
  setTimeout(function () {
    // f1的任務(wù)代碼
    callback();
  }, 1000);
}
f1(f2);

采用這種方式,我們把同步操作變成了異步操作,f1不會堵塞程序運行,相當于先執(zhí)行程序的主要邏輯,將耗時的操作推遲執(zhí)行。
回調(diào)函數(shù)的優(yōu)點是簡單、容易理解和部署,缺點是不利于代碼的閱讀和維護,各個部分之間高度耦合(Coupling),流程會很混亂,而且每個任務(wù)只能指定一個回調(diào)函數(shù)。

事件監(jiān)聽

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

f1.on("done", f2);
function f1(){
  setTimeout(function () {
    // f1的任務(wù)代碼
    f1.trigger("done");
  }, 1000);
}

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

Promises對象

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

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

已經(jīng)完成了 resolved

因為某種原因被中斷了 rejected

還在等待上一個事務(wù)結(jié)束 pending

簡單說,它的思想是,每一個異步任務(wù)返回一個Promises對象,該對象有一個then方法,允許指定回調(diào)函數(shù),這樣寫的優(yōu)點在于,回調(diào)函數(shù)變成了鏈式寫法,程序的流程可以看得很清楚

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

Promises可以分為:

無錯誤傳遞的 Promises,也就是事務(wù)不會因為任何原因中斷,事務(wù)隊列中的事項都會被依次處理,此過程中 Promises只有 pendingresolved兩種狀態(tài),沒有 rejected狀態(tài)。

包含錯誤的 Promises,每個事務(wù)的處理都必須使用容錯機制來獲取結(jié)果,一旦出錯,就會將錯誤信息傳遞給下一個事務(wù),如果錯誤信息會影響下一個事務(wù),則下一個事務(wù)也會 rejected,如果不會,下一個事務(wù)可以正常執(zhí)行,依次類推。

此處留坑講generator實現(xiàn)異步編程
本來想自己總結(jié)下generator與異步的,看了下阮一峰老師的博客算是了解個大概,理解也是一知半解,有興趣的話可以在底下的參考資料里找到去看看

封裝好的實現(xiàn) jqueryDeferred對象

簡單說,Deferred對象就是jquery的回調(diào)函數(shù)解決方案。在英語中,defer的意思是"延遲",所以Deferred對象的含義就是"延遲"到未來某個點再執(zhí)行。
首先,回顧一下jquery的ajax操作的傳統(tǒng)寫法:

  $.ajax({
    url: "test.html",
    success: function(){
      alert("哈哈,成功了!");
    },
    error:function(){
      alert("出錯啦!");
    }
  });

有了Deferred對象以后,寫法是這樣的:

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

可以看到,done()相當于success方法,fail()相當于error方法。采用鏈式寫法以后,代碼的可讀性大大提高。
了解jQuery.Deferred對象可以看下面這個表格。

when.js

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

var getData = function() {
    var deferred = when.defer();
    $.getJSON(api, function(data){
        deferred.resolve(data[0]);
    });

    return deferred.promise;
}

var getImg = function(src) {
    var deferred = when.defer();

    var img = new Image();

    img.onload = function() {
        deferred.resolve(img);
    };

    img.src = src;

    return deferred.promise;
}

var showImg = function(img) {
    $(img).appendTo($("#container"));
}

getData()
.then(getImg)
.then(showImg);

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

var deferred = when.defer();

定義了一個deferred對象。

deferred.resolve(data);

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

return deferred.promise;

返回了deferred對象的Promises屬性。

控制流程工具step.js

github地址
step.js是控制流程工具(大小僅 150 行代碼),解決回調(diào)嵌套層次過多等問題。適用于讀文件、查詢數(shù)據(jù)庫等回調(diào)函數(shù)相互依賴,或者分別獲取內(nèi)容最后組合數(shù)據(jù)返回等應(yīng)用情景。異步執(zhí)行簡單地可以分為“串行執(zhí)行”和“并行”執(zhí)行
使用示例:

Step(
  function readSelf() {
    fs.readFile(__filename, this);
  },
  function capitalize(err, text) {
    if (err) throw err;
    return text.toUpperCase();
  },
  function showIt(err, newText) {
    if (err) throw err;
    console.log(newText);
  }
);

Step 的一個約定,回調(diào)函數(shù)的第一個參數(shù)總是 err,第二個才是值(沿用 Node 回調(diào)的風格)。如果上一個步驟發(fā)生異常,那么異常對象將被送入到下一個步驟中。

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

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

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

Answer2: JS的單線程是指一個瀏覽器進程中只有一個JS的執(zhí)行線程,同一時刻內(nèi)只會有一段代碼在執(zhí)行(你可以使用IE的標簽式瀏覽試試看效果,這時打開的多個頁面使用的都是同一個JS執(zhí)行線程,如果其中一個頁面在執(zhí)行一個運算量較大的function時,其他窗口的JS就會停止工作)。
而異步機制是瀏覽器的兩個或以上常駐線程共同完成的,例如異步請求是由兩個常駐線程:JS執(zhí)行線程和事件觸發(fā)線程共同完成的,JS的執(zhí)行線程發(fā)起異步請求(這時瀏覽器會開一條新的HTTP請求線程來執(zhí)行請求,這時JS的任務(wù)已完成,繼續(xù)執(zhí)行線程隊列中剩下的其他任務(wù)),然后在未來的某一時刻事件觸發(fā)線程監(jiān)視到之前的發(fā)起的HTTP請求已完成,它就會把完成事件插入到JS執(zhí)行隊列的尾部等待JS處理。又例如定時觸發(fā)(setTimeoutsetinterval)是由瀏覽器的定時器線程執(zhí)行的定時計數(shù),然后在定時時間把定時處理函數(shù)的執(zhí)行請求插入到JS執(zhí)行隊列的尾端(所以用這兩個函數(shù)的時候,實際的執(zhí)行時間是大于或等于指定時間的,不保證能準確定時的)。
所以,所謂的JS的單線程和異步更多的應(yīng)該是屬于瀏覽器的行為,他們之間沒有沖突,更不是同一種事物,沒有什么區(qū)別不區(qū)別的。

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

首先,不會立即執(zhí)行,原因:
setTimeout(fn,0)的作用很簡單,就是為了把fn放到運行隊列的最后去執(zhí)行。也就是說,無論setTimeout(fn,0)寫在哪,都可以保證在隊列的最后執(zhí)行。js解析器會把setTimeout(fn,0)里的fn壓到隊列的最后,因為它是異步操作。有個延時,具體是16ms還是4ms取決于瀏覽器
立即執(zhí)行還是有可能的,只要在你調(diào)用setTimeout的時候,滿足下面兩個條件:

剛好執(zhí)行到了當前這一輪事件循環(huán)的底部。

剛好此時事件隊列為空。

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

參考資料

知乎:setTimeout的異步以及js是單線程的面試題?
阮老師的博客
小胡子哥的博客
知乎:JavaScript 既是單線程又是異步的,請問這二者是否沖突,以及有什么區(qū)別?
細嗅Promise
whenjs文檔
jQuery的deferred對象詳解
jQuery 中的 Deferred 和 Promises (2)
Step.js 使用教程(附源碼解析)
Generator 函數(shù)的含義與用法

文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉(zhuǎn)載請注明本文地址:http://systransis.cn/yun/80333.html

相關(guān)文章

  • JavaScript 異步

    摘要:從最開始的到封裝后的都在試圖解決異步編程過程中的問題。為了讓編程更美好,我們就需要引入來降低異步編程的復(fù)雜性。寫一個符合規(guī)范并可配合使用的寫一個符合規(guī)范并可配合使用的理解的工作原理采用回調(diào)函數(shù)來處理異步編程。 JavaScript怎么使用循環(huán)代替(異步)遞歸 問題描述 在開發(fā)過程中,遇到一個需求:在系統(tǒng)初始化時通過http獲取一個第三方服務(wù)器端的列表,第三方服務(wù)器提供了一個接口,可通過...

    tuniutech 評論0 收藏0
  • ES6-7

    摘要:的翻譯文檔由的維護很多人說,阮老師已經(jīng)有一本關(guān)于的書了入門,覺得看看這本書就足夠了。前端的異步解決方案之和異步編程模式在前端開發(fā)過程中,顯得越來越重要。為了讓編程更美好,我們就需要引入來降低異步編程的復(fù)雜性。 JavaScript Promise 迷你書(中文版) 超詳細介紹promise的gitbook,看完再不會promise...... 本書的目的是以目前還在制定中的ECMASc...

    mudiyouyou 評論0 收藏0
  • JS筆記

    摘要:從最開始的到封裝后的都在試圖解決異步編程過程中的問題。為了讓編程更美好,我們就需要引入來降低異步編程的復(fù)雜性。異步編程入門的全稱是前端經(jīng)典面試題從輸入到頁面加載發(fā)生了什么這是一篇開發(fā)的科普類文章,涉及到優(yōu)化等多個方面。 TypeScript 入門教程 從 JavaScript 程序員的角度總結(jié)思考,循序漸進的理解 TypeScript。 網(wǎng)絡(luò)基礎(chǔ)知識之 HTTP 協(xié)議 詳細介紹 HTT...

    rottengeek 評論0 收藏0
  • JS進階篇--RequireJS模塊化編程詳解

    摘要:所有依賴這個模塊的語句,都定義在一個回調(diào)函數(shù)中,等到加載完成之后,這個回調(diào)函數(shù)才會運行。 1.模塊的寫法 模塊化編程一般都有這么幾個過渡過程,如下描述。 原始方法 function m1(){   //... } function m2(){   //... } 上面的函數(shù)m1()和m2(),組成一個模塊。使用的時候,直接調(diào)用就行了。 這種做法的缺點很明顯:污染了全局變量,無法保證不與...

    妤鋒シ 評論0 收藏0
  • 前端_JavaScript

    摘要:為此決定自研一個富文本編輯器。例如當要轉(zhuǎn)化的對象有環(huán)存在時子節(jié)點屬性賦值了父節(jié)點的引用,為了關(guān)于函數(shù)式編程的思考作者李英杰,美團金融前端團隊成員。只有正確使用作用域,才能使用優(yōu)秀的設(shè)計模式,幫助你規(guī)避副作用。 JavaScript 專題之惰性函數(shù) JavaScript 專題系列第十五篇,講解惰性函數(shù) 需求 我們現(xiàn)在需要寫一個 foo 函數(shù),這個函數(shù)返回首次調(diào)用時的 Date 對象,注意...

    Benedict Evans 評論0 收藏0

發(fā)表評論

0條評論

最新活動
閱讀需要支付1元查看
<