同步模式與異步模式面試
實現JavaScript異步編程的幾種方式編程
最先JavaScript
語言就是運行在瀏覽器端的語言,目的是爲了實現頁面上的動態交互。實現頁面交互的核心就是DOM
操做,這就決定了它必須使用單線程模型,不然就會出現很複雜的線程同步問題。segmentfault
假設在js
中有多個線程一塊兒工做,其中一個線程修改了這個DOM
元素,同時另外一個線程又刪除了這個元素,此時瀏覽器就沒法明確該以哪一個工做線程爲準。因此爲了不線程同步的問題,從一開始,js
就設計成了單線程的工做模式。
因此,js是單線程工做模式,若是有多個任務,任務須要排隊,一個一個依次去執行。瀏覽器
爲了解決這種問題,js有兩種任務的執行模式:安全
同步模式(Synchronous) 和 異步模式(Asynchronous)。異步
指的是代碼的任務依次執行,後一個任務必須等待前一個任務結束才能開始執行。程序的執行順序和代碼的編寫順序是徹底一致的。在單線程模式下,大多數任務都會以同步模式執行。async
console.log('global begin') function bar () { console.log('bar task') } function foo () { console.log('foo task') bar() } foo() console.log('global end') // global begin // foo task // bar task //global end // 使用調用棧的邏輯
爲了不耗時函數讓頁面卡頓和假死,因此還有異步模式。異步編程
該模式不會去等待這個任務的結束纔開始下一個任務,都是開啓事後就當即日後執行下一個任務,此時異步線程會單獨執行異步任務,耗時函數的後續邏輯會經過回調函數的方式定義,執行事後會將回調放到消息隊列中,js主線程執行完任務事後會依次執行消息隊列中的任務。這裏要強調,js是單線程的,瀏覽器不是單線程的,有一些API是有單獨的線程去作的。函數
下面看一個簡單的異步模式的例子:線程
console.log('global begin') // 延時器 setTimeout(function timer1 () { console.log('timer1 invoke') }, 1800) // 延時器中又嵌套了一個延時器 setTimeout(function timer2 () { console.log('timer2 invoke') setTimeout(function inner () { console.log('inner invoke') }, 1000) }, 1000) console.log('global end') // global begin // global end // timer2 invoke // timer1 invoke // inner invoke //除了調用棧,還用到了消息隊列和事件循環
異步模式對於JavaScript
語言很是重要,沒有它就沒法同時處理大量的耗時任務。對於開發者而言。單線程下面的異步最大的難點就是代碼執行的順序混亂 ,因此面試題裏面百分百會考這裏的內容 - -|||
。
同步模式的API的特色:任務執行完代碼纔會繼續往下走,例如:console.log
異步模式的API的特色:下達這個任務開啓的指令以後代碼就會繼續執行,代碼不會等待任務的結束
回調函數:由調用者定義,交給執行者執行的函數
// callback就是回調函數 // 就是把函數做爲參數傳遞,缺點是不利於閱讀,執行順序混亂。 function foo(callback) { setTimeout(function(){ callback() }, 3000) } foo(function() { console.log('這就是一個回調函數') console.log('調用者定義這個函數,執行者執行這個函數') console.log('其實就是調用者告訴執行者異步任務結束後應該作什麼') })
還有其餘的一些實現異步的方式,例如:事件機制和發佈訂閱。這些也都是基於回調函數之上的變體。
主要爲了解決回調地獄問題,詳細瞭解參考 Promise(更優的異步編程解決方案)
詳細瞭解參考 Generator -> Generator異步方案