JS非同步
-
單執行緒
JS是單執行緒,一次只能做一件事,如果同一時間有多個任務的話,這些任務需排隊,前一個任務執行完才會執行後一個任務。
JS為什麼是單執行緒,這與JS用途有很大關係,JS是瀏覽器指令碼語言,主要用來實現與使用者互動,利用JS可以實現對DOM的各種操作,如果是多執行緒會帶來很複雜的同步問題
-
為什麼需要非同步
JS 為單執行緒同一時間只能處理同個任務,但如果前一個任務執行時間很長,如檔案讀取和 ajax 操作,後一個任務就不得不等著,嚴重影響使用者體驗。JS 的非同步就是先掛起處於等待中的任務,先執行排在後面的任務,等到檔案讀取或 ajax 有了結果後再回頭執行掛起的任務
非同步任務指不進入主執行緒,而進入任務佇列的任務,只有任務佇列通知主執行緒,某個非同步任務可以執行了,該任務才會進入主執行緒,如圖片、音樂的載入。
-
非同步機制
非同步如何實現,回撥和事件迴圈。
任務佇列:非同步任務不會進入主執行緒,而是先進入任務佇列,任務佇列是一個先進先出的資料結構,一個非同步任務結束就會在任務佇列中新增事件,即可以進入執行棧。但此時的主執行緒不一定有空,當主執行緒處理完其他任務佇列有空時,就會讀取任務佇列,讀取裡面有哪些事件,前面的事件會被優先處理,如果該任務指定了回撥函式,就會執行回撥函式中的程式碼。
單執行緒從任務佇列中讀取任務是不斷迴圈的,每次棧被清空後,都會在任務佇列中的讀取新的任務,這就叫做任務迴圈,因每個任務都由一個事件觸發,因此也叫事件迴圈。
JS非同步機制步驟
- 所有同步任務都在主執行緒上執行,形成執行棧
- 主執行緒外,還存在一個任務隊裡,只有非同步任務有了結果,就會在任務佇列中放置一個事件
- 一旦執行棧中的所有同步任務執行完畢,系統就會讀取任務佇列,看看裡面有哪些事件,對應的非同步任務結束等待狀態,進入執行棧,開始執行。
- 主執行緒不斷重複第三步
-
非同步程式設計
-
回撥函式
一個函式(回撥函式)作為引數傳遞給另一個函式,並在函式中被呼叫
使用 ajax 時,我們就用到了回撥函式實現的非同步
var req = new XMLHttpRequest() req.open("GET",url) req.send(null) //非同步任務 req.Onreadystatechange = function(){} //事件回撥,得到相應之後觸發該事件
還要像
setTimeout
等回撥函式容易形成回撥煉獄,不利於程式碼閱讀
-
Promise
ES6.0 新增的非同步物件
-
Promise 是一個建構函式,既然是建構函式,那麼我們就可以 new Promise() 得到一個 Promise 例項
-
在 Promise 上,有兩個函式,分別叫做 resolve (成功之後的回撥函式) 和 reject (失敗之後的回撥函式)
-
在 Promise 建構函式的 Prototype 屬性上,有一個 .then() 方法,也就是說,只要是 Promise 函式構造建立的例項,都可以訪問到 .then方法
-
Promise 表示一個非同步操作,當我們 new Promise 例項,這個例項就表示一個具體的非同步操作
-
既然 Promise 建立的例項是一個非同步操作,那麼這個非同步操作的結果,只能有兩種狀態:
-
狀態1:非同步執行成功了,需要在內部呼叫成功的回撥函式 resolve 把結果返回給呼叫者
-
狀態2:非同步執行失敗了,需要在內部呼叫失敗的回撥函式 reject 把結果返回給呼叫者
-
由於 Promise 例項是一個非同步操作,所以內部拿到操作的結果後,無法使用 return 把操作的結果返回個呼叫者;這時候,只能使用回撥函式的形式,來把成功或失敗的結果,返回給呼叫者
-
-
們可以在 new 出來的 Promise 例項上呼叫.then() 方法,預先為這個 Promise 非同步操作,指定成功和失敗的回撥函式
-
-
用 Promise 封裝讀取檔案操作
function getFileByPath(fpath){ return new Promise(function (resolve,reject) { fs.readFile(fpath, 'utf-8', (err,dataStr) => { if(err) return reject(err) resolve(dataStr) }) }) } getFileByPath('./11.txt').then(function(data){ console.log(data+'訪問成功') },function (err) { console.log(err.message+'訪問失敗') })
-