自從讀了研後,走上了學術之路,天天除了看論文就是作實驗,最後發現本身仍是喜歡開發呀,因而我又重回前端啦~javascript
隔了這麼久沒學前端,好像不少東西都忘了很多,並且不得不說前端的技術更新是真的快,接下來將會從新拾起前端的一點一滴,首先進入的是js的同步和異步的世界~前端
1、單線程java
(1)單線程的概念 ajax
若是你們熟悉java,應該都知道,java是一門多線程語言,咱們經常能夠利用java的多線程處理各類各樣的事,好比說文件上傳,下載等,而JavaScript是否也能夠支持多線程呢?chrome
答案是否認的,JavaScript是一門單線程的語言,所以,JavaScript在同一個時間只能作一件事,單線程意味着,若是在同個時間有多個任務的話,這些任務就須要進行排隊,前一個任務執行完,纔會執行下一個任務,好比說下面這段代碼編程
// 同步代碼 function fun1() { console.log(1); } function fun2() { console.log(2); } fun1(); fun2(); // 輸出 1 2
很容易能夠看出,輸出會依次輸入1,2,由於代碼是從上到下依次執行,執行完fun1(),才繼續執行fun2(),可是若是fun1()中的代碼執行的是讀取文件或者ajax操做,文件的讀取和數據的獲取都須要必定時間,難道咱們須要徹底等到fun1()執行完才能繼續執行fun2()麼?爲了解決這個問題,後面咱們會介紹同步和異步的概念promise
(2)爲何是單線程瀏覽器
其實,JavaScript的單線程,與它的用途是有很大關係,咱們都知道,JavaScript做爲瀏覽器的腳本語言,主要用來實現與用戶的交互,利用JavaScript,咱們能夠實現對DOM的各類各樣的操做,若是JavaScript是多線程的話,一個線程在一個DOM節點中增長內容,另外一個線程要刪除這個DOM節點,那麼這個DOM節點到底是要增長內容仍是刪除呢?這會帶來很複雜的同步問題,所以,JavaScript是單線程的服務器
2、同步任務和異步任務數據結構
(1)爲何會有同步和異步
由於JavaScript的單線程,所以同個時間只能處理同個任務,全部任務都須要排隊,前一個任務執行完,才能繼續執行下一個任務,可是,若是前一個任務的執行時間很長,好比文件的讀取操做或ajax操做,後一個任務就不得不等着,拿ajax來講,當用戶向後臺獲取大量的數據時,不得不等到全部數據都獲取完畢才能進行下一步操做,用戶只能在那裏乾等着,嚴重影響用戶體驗
所以,JavaScript在設計的時候,就已經考慮到這個問題,主線程能夠徹底不用等待文件的讀取完畢或ajax的加載成功,能夠先掛起處於等待中的任務,先運行排在後面的任務,等到文件的讀取或ajax有告終果後,再回過頭執行掛起的任務,所以,任務就能夠分爲同步任務和異步任務
(2)同步任務
同步任務是指在主線程上排隊執行的任務,只有前一個任務執行完畢,才能繼續執行下一個任務,當咱們打開網站時,網站的渲染過程,好比元素的渲染,其實就是一個同步任務
(3)異步任務
異步任務是指不進入主線程,而進入任務隊列的任務,只有任務隊列通知主線程,某個異步任務能夠執行了,該任務纔會進入主線程,當咱們打開網站時,像圖片的加載,音樂的加載,其實就是一個異步任務
function fun1() { console.log(1); } function fun2() { console.log(2); } function fun3() { console.log(3); } fun1(); setTimeout(function(){ fun2(); },0); fun3(); // 輸出 1 3 2
有了異步,就算fun2()裏面是文件的讀取或ajax這種須要耗時的任務,也不怕fun3()要等到fun2()執行完才能執行啦
(4)異步機制
那麼,JavaScript中的異步是怎麼實現的呢?那要須要說下回調和事件循環這兩個概念啦
首先要先說下任務隊列,咱們在前面也介紹了,異步任務是不會進入主線程,而是會先進入任務隊列,任務隊列實際上是一個先進先出的數據結構,也是一個事件隊列,好比說文件讀取操做,由於這是一個異步任務,所以該任務會被添加到任務隊列中,等到IO完成後,就會在任務隊列中添加一個事件,表示異步任務完成啦,能夠進入執行棧啦~可是這時候呀,主線程不必定有空,當主線程處理完其它任務有空時,就會讀取任務隊列,讀取裏面有哪些事件,排在前面的事件會被優先進行處理,若是該任務指定了回調函數,那麼主線程在處理該事件時,就會執行回調函數中的代碼,也就是執行異步任務啦
單線程從從任務隊列中讀取任務是不斷循環的,每次棧被清空後,都會在任務隊列中讀取新的任務,若是沒有任務,就會等到,直到有新的任務,這就叫作任務循環,由於每一個任務都是由一個事件觸發的,所以也叫做事件循環
總的來講,JavaScript的異步機制包括如下幾個步驟
(1)全部同步任務都在主線程上執行,行成一個執行棧 (2)主線程以外,還存在一個任務隊列,只要異步任務有告終果,就會在任務隊列中放置一個事件 (3)一旦執行棧中的全部同步任務執行完畢,系統就會讀取任務隊列,看看裏面還有哪些事件,那些對應的異步任務,因而結束等待狀態,進入執行棧,開始執行 (4)主線程不斷的重複上面的第三步
3、異步編程
那麼,怎麼才能實現異步編程,寫出性能更好的代碼呢,下面有幾種方式
(1)回調函數
回調函數是實現異步編程最簡單的方法啦,回調函數咱們在使用ajax時應該用的不少啦,其實在使用ajax時,咱們就用到了異步
var req = new XMLHttpRequest(); req.open("GET",url); req.send(null); req.onreadystatechange=function(){}
req.send()方法是 AJAX 向服務器發生數據,它是一個異步任務,而 req.onreadystatechange()屬於事件回調,藉由瀏覽器的HTTP請求線程發起對服務器的請求,在請求獲得響應以後觸發請求完成事件,將回調函數推入事件隊列等待執行
其實像setTimeout,還有咱們平時爲元素綁定監聽事件,和上面說的道理也是同樣的
回調函數的優勢是簡單、容易理解和部署,缺點是不利於代碼的閱讀和維護,各個部分之間高度耦合(Coupling),流程會很混亂,並且每一個任務只能指定一個回調函數
(2)Promise
一直以來,JavaScript處理異步都是以callback的方式,在前端開發領域callback機制幾乎深刻人心,近幾年隨着JavaScript開發模式的逐漸成熟,CommonJS規範順勢而生,其中就包括提出了Promise規範,Promise徹底改變了js異步編程的寫法,讓異步編程變得十分的易於理解,同時Promise也已經歸入了ES6,並且高版本的chrome、firefox瀏覽器都已經原生實現了Promise,只不過和現現在流行的類Promise類庫相比少些API
Promise包括如下幾個規範
then
方法(能夠說,then就是promise的核心),並且then必須返回一個promise,同一個promise的then能夠調用屢次,而且回調的執行順序跟它們被定義時的順序一致在使用Promise時,咱們須要檢測一些瀏覽器是否支持Promise
if(typeof(Promise)==="function") { console.log("支持"); } else { console.log("不支持"); }
咱們可使用new Promise進行Promise的建立
function wait(time) { return new Promise(function(resolve,reject) { setTimeout(resolve,time); }); }
這個時候咱們就可使用Promise處理異步任務啦
wait(1000).then(function(){ console.log(1); })
上面這個例子表示1秒後輸出1,一樣的道理,咱們可使用Promise進行更加複雜的操做,關於更多的操做,就不繼續說啦,關於異步的實現,其實還有其它的一些方法,可是由於上面說的這兩種方法用的比較多,因此就只說上面這兩種了