2014年秋季寫完了《深刻理解javascript原型和閉包系列》,已經幫助過不少人走出了 js 原型、做用域、閉包的困惑,至今仍能常常受到好評的留言。javascript
很早以前我就總結了JS三座大山這個概念(雖然沒有處處宣揚),前兩座(原型、做用域)已經基本講明白,而第三座(異步)也應該作一個總結。html
因而,2017年初春,我花費大約一週的業餘時間來對 JS 異步作一個完整的總結,和各位同窗共勉共進步!前端
原文地址:http://www.cnblogs.com/wangfupeng1988/p/6513070.html 未經贊成禁止轉載!java
提醒:若是你是初學 js 的同窗,還沒有有太多項目經驗和基礎知識,請就此打住,不要看這篇教程node
我思考問題、寫文章通常都不按討論出牌,別人寫過的東西我不會再照着抄一遍。所以,後面全部的內容,都是我看了許多資料以後,我的從新思考提煉總結出來的,這確定不能算是初級教程。git
若是你是已有 js 開發經驗,並瞭解異步的基礎知識,到這裏來想深刻了解一下Promise
Generator
和async-awati
,那就太好了,很是歡迎。github
首先記住一句話 —— JS 是單線程的語言,所謂「單線程」就是一根筋,對於拿到的程序,一行一行的執行,上面的執行爲完成,就傻傻的等着。例如web
var i, t = Date.now() for (i = 0; i < 100000000; i++) { } console.log(Date.now() - t) // 250 (chrome瀏覽器)
上面的程序花費 250ms 的時間執行完成,執行過程當中就會有卡頓,其餘的事兒就先撂一邊無論了。面試
執行程序這樣沒有問題,可是對於 JS 最初使用的環境 ———— 瀏覽器客戶端 ———— 就不同了。所以在瀏覽器端運行的 js ,可能會有大量的網絡請求,而一個網絡資源啥時候返回,這個時間是不可預估的。這種狀況也要傻傻的等着、卡頓着、啥都不作嗎?———— 那確定不行。ajax
所以,JS 對於這種場景就設計了異步 ———— 即,發起一個網絡請求,就先無論這邊了,先幹其餘事兒,網絡請求啥時候返回結果,到時候再說。這樣就能保證一個網頁的流程運行。
先看一段比較常見的代碼
var ajax = $.ajax({ url: '/data/data1.json', success: function () { console.log('success') } })
上面代碼中$.ajax()
須要傳入兩個參數進去,url
和success
,其中url
是請求的路由,success
是一個函數。這個函數傳遞過去不會當即執行,而是等着請求成功以後才能執行。對於這種傳遞過去不執行,等出來結果以後再執行的函數,叫作callback
,即回調函數
再看一段更加能說明回調函數的 nodejs 代碼。和上面代碼基本同樣,惟一區別就是:上面代碼時網絡請求,而下面代碼時 IO 操做。
var fs = require('fs') fs.readFile('data1.json', (err, data) => { console.log(data.toString()) })
從上面兩個 demo 看來,實現異步的最核心原理,就是將callback
做爲參數傳遞給異步執行函數,當有結果返回以後再觸發 callback
執行,就是如此簡單!
開發中比較經常使用的異步操做有:
ajax
http.get
readFile
readdir
setTimeout
setInterval
最後,請思考,事件綁定是否是也是異步操做?例如$btn.on('click', function() {...})
。這個問題頗有意思,我會再後面的章節通過分析以後給出答案,各位先本身想一下。
提到異步,就必須提 event-loop 。event-loop 中文翻譯叫作「事件輪詢」,它是能體現出單線程中異步操做是如何被執行的。
首先,強烈你們觀看一個歪果仁的視頻《what the hack is event loop》,只有不到半個小時的時間,可是將的很是詳細。若是那個連接失效,訪問這裏(密碼: xx9f)
其次,再結合阮一峯老師的《什麼是event loop》一塊兒看一下。將這兩個看完就基本瞭解 event loop 了
最後,event-loop 是一塊內容比較獨立的技術性知識,它是什麼樣子就是什麼樣子,講解起來可變通性很是小。所以,本節說一下我對 event-loop 的理解和體會
給出一段簡單的 js 代碼,並用比較通俗、簡單的說法介紹一下執行過程。詳細過程還需各位去看視頻,由於我不必把半小時的視頻都寫到這裏。
console.log('line 1') setTimeout(console.log, 1000, 'line 2') console.log('line 3')
以上一共三行代碼,該程序被執行的時候,會依次挨行執行
line 1
打印出來setTimeout
是一個異步執行操做。line 3
打印出出來以上只拿了setTimeout
舉例子,可是對於網絡請求、IO操做、事件綁定道理都是同樣的。若是我講的簡單例子你仍是看不懂,必定要去看文章最初提到的《what the hack is event loop》視頻,重要重要!!!
第一題,如下代碼的輸出順序是什麼
setTimeout(console.log, 0, 'a') console.log('b') console.log('c')
答案是b c a
,有疑問的須要再去看上面的介紹或者那個視頻。
第二題,如下代碼中,最後輸出的結果是不是 500
var i, t = Date.now() for (i = 0; i < 100000000; i++) { } function fn() { console.log(Date.now() - t) // 輸出多少??? } setTimeout(fn, 500)
答案是大於 500ms ,由於 for 函數須要花費一些時間,等 for 執行完以後再開始計算 500ms 以後執行 fn
第三題,事件綁定是否是異步操做?
這個問題你們根據 event-loop 的講解和視頻來思考,咱們下一節再給出解答。
若是你認真看了上一節的 event-loop 的,你會發現原來事件綁定和異步操做的實現機制是同樣的,那麼事件綁定是否是就是異步操做呢?(聲明一下,這裏說的事件綁定是以下代碼的形式)
$btn.on('click', function (e) { console.log('你點擊了按鈕') })
PS:這個問題貌似沒有加過有人討論或者發起討論,可是當我瞭解了 event-loop 以後,我就發現這二者有很大聯繫,很早就像討論一下這個話題。不知道哪位同仁跟我有同樣的想法?
從技術實現以及書寫方法上來說,他們是同樣的。例如事件綁定和 IO 操做的寫法基本相同
$btn.on('click', function (e) { console.log('你點擊了按鈕') }) fs.readFile('data1.json', function (err, data) { // 獲取數據 })
最終執行的方式也基本同樣,都經過 evet-loop 執行。
在我看來至少有兩處不一樣。
第一,event-loop 執行時,調用的源不同。異步操做是系統自動調用,不管是setTimeout
時間到了仍是$.ajax
請求返回了,系統會自動調用。而事件綁定就須要用戶手動觸發
第二,從設計上來將,事件綁定有着明顯的「訂閱-發佈」的設計模式,而異步操做卻沒有。
我我的看代碼比較偏重設計,一個東西是什麼要看它是未什麼而設計的。所以,我傾向於事件綁定不是異步操做。雖然它也是經過 event-loop 實現調用的,可是它的設計目錄卻和異步操做徹底不同。
其實,事件綁定在 js 中扮演着很是重要的角色,各個地方都會用到事件綁定的形式。例如 web 頁面監控鼠標、鍵盤,以及 nodejs 中的 EventEmitter
應用很是普遍(特別是涉及到數據流時)。而事件綁定被應用到很是普遍,卻沒有發生像異步操做帶來的程序邏輯問題,反而你們用的很是開心————這又一個二者不同的例證。
若是你以爲個人觀點有問題,也能夠大膽提出本身的建議和意見,發表出來!說對說錯都無所謂,也不會扣你落戶積分,只要能自圓其說就是好的。
若是你看完了,感受還不錯,歡迎給我打賞 ———— 以激勵我更多輸出優質內容
最後,github地址是 https://github.com/wangfupeng1988/js-async-tutorial 歡迎 star 和 pr
-------
學習做者教程:《前端JS高級面試》《前端JS基礎面試題》《React.js模擬大衆點評webapp》《zepto設計與源碼分析》《json2.js源碼解讀》