Javascript的異步和回調

引子

每一個故事都有由來。前兩天在看 gulp 的時候,看到了它有個 promise 的玩意兒,而後的而後,這兩天就掉進了 javascript 的異步和回調的坑裏面去了。
其間搜索了 javascript promise,看到了一堆好文章。大概給個 List 吧。javascript

看得昏天黑地,大概也理清楚了一點,作個小總結。html

一些概念

上面這些文章寫得都挺好,但大部分都是上來直接說怎麼異步回調,js的異步有哪些方法。這適合高級選手。我剛開始連啥是異步,啥是回調都不太清楚,這些方法天然也比較難理解。因此仍是打好基礎,先弄清楚異步、回調這些基本概念比較好。java

同步與異步

先看個例子。gulp

javascriptfoo();
bar();
  • 程序運行通常是同步的(synchronous),即按照書寫的順序執行。在上述例子中,bar 方法會在 foo 方法執行完以後,再執行。
  • 異步(asynchronous)與同步相對,即在前一個方法未執行完時,就開始運行後一個方法。在上述例子中,先執行 foo 方法,foo 方法沒執行完,就開始執行 bar 方法。
  • 總而言之,同步就是順序執行,異步就是不徹底按順序執行。

異步的好處

從異步的概念中能夠發現,程序異步運行,能夠提升程序運行的效率,沒必要等一個程序跑完,再跑下一個程序,特別當這兩個程序是無關的時候。兩個程序在必定時間內,能夠是同時運行的。寫服務器的時候應該會碰到不少這樣的例子。能夠想象,若是服務器的程序都是同步的,那併發什麼的就不存在了吧。segmentfault

阻塞與非阻塞

這一點是我本身簡單的理解。promise

  • 阻塞就是說一個程序沒運行完,它後面的程序是沒法運行的。
  • 非阻塞則相反,一個程序若是由於各類緣由(網絡、代碼量等)沒運行完的時候,其餘的程序也是能夠繼續運行的。

單線程與多線程

這一點也是我本身的簡單理解。服務器

  • 單線程是指程序運行只有一個通道,不一樣的方法須要排隊執行。
  • 而不少語言均可以提供多線程的功能,至關於開了幾個通道運行程序,使得程序能夠在不一樣的線程中運行,不會相互影響。

多線程、非阻塞、異步

從上述基本概念中能夠發現,異步若是發生在多線程語言中,會十分天然且符合邏輯。異步本質上應該就是多線程語言的產物。由於只有在多線程語言中才可以實現程序之間相互不干擾,不產生阻塞。網絡

JS 中的異步

有了上面的一些基本概念,那麼下面來講說正題,JS中的異步。
咱們都知道 JS 是一個單線程的語言,永遠只有一個通道在運行程序。那麼既然它是個單線程又如何會有異步呢?
JS 中所謂的異步,應該被稱爲僞異步(pseudo asynchronous)。這是由於 JS 語言中的異步,會產生阻塞,並會相互干擾。多線程

模擬 JS 中異步的方法 —— setTimeout

咱們來看一下 setTimeout 如何模擬 JS 中的異步。併發

javascriptvar foo = function(){
    console.log('foo begins');
    setTimeout(function(){
        console.log('foo finishes');
    },1000);
};
var bar = function(){
    console.log('bar executed');  
}
foo();
bar();

上述過程執行的時候,會打印出

foo begins
bar executed
foo finishes

因此,在上述代碼塊中,在前一方法(foo)執行時,後一方法(bar)也能夠執行。符合異步的基本概念,程序並不按順序執行。
說是模擬是由於,你能夠把 console.log('foo begins'); 理解成會運行 1 秒的一個代碼行,運行完後,會跳出foo finishes。而中間這 1 秒運行的時候,後面的 bar 方法也是能夠運行的。這樣就模擬了一個異步的效果。

JS 中異步的方法存在的問題 —— 阻塞與干擾

咱們將上述代碼塊稍作修改

javascriptvar foo = function(){
    console.log('foo begins');
    setTimeout(function(){
        console.log('foo finishes');
    },1000);
};
var bar = function(){

    while(){

    } 
}
foo();
bar();

你會發現 1 秒以後 foo finishes 並無被打印出來。這是由於 bar 方法是個死循環,使得 js 引擎假死,致使了 foo 方法也沒有被運行完。若是是多線程的異步,假死的應該是運行 bar 方法的線程,而 foo 方法仍然會按預期打印出 foo finishes。固然了,其實這個死循環也只是模擬 bar 方法塊程序運行的時間將很長。實際上,若是 bar 方法運行的時間超過了 1 秒,比方說是 5 秒,那麼 foo finishes 也將在 5 秒以後被打印出來。這個本質上取決於 JS 單線程程序塊按隊列執行的特性。
因此 JS 中的異步並不能像普通的異步同樣,實現非阻塞和不干擾。

JS 中異步的一些實現方法

雖然 JS 中的異步有其先天的缺陷,可是這種異步的思想,仍然能被 JS 程序開發人員所借鑑。畢竟,異步是能夠大大提升程序運行效率的。
也正是因爲 JS 自己是單線程程序的關係,因此 JS 中異步的實現,並不能像其餘語言同樣,簡單地多開個線程就能夠解決。
目前我看的集中方法主要有回調、事件類方法、promise等。

回調

先說說回調是什麼吧。
回調(callback)這種名詞就跟函數(function)同樣,乍一看是比較難懂的,至少我是這樣的。
根據sf上這個問答的解釋,能夠明確,把一個函數做爲參數傳入到另外一個函數中,那麼這個做爲參數的函數就叫作回調函數。如:

javascriptvar foo = function(callback){
    // foo method
    callback();
};
foo(bar);

其中,bar 就是一個回調函數。固然了,按我我的的理解,應該說是 bar 是 foo 的回調函數。

To be Continued

時間問題,具體的實現方式和理解還沒好好看,往後再作細細梳理。上述理解若有偏頗,歡迎討論指正。

相關文章
相關標籤/搜索