1. 程式人生 > >js基礎 從setTimeout 到 js的非同步原理(面試題)

js基礎 從setTimeout 到 js的非同步原理(面試題)

  都是一些很基礎但是很容易被忽視的問題,大家知道這個方法怎麼使用但是並沒有深入研究到底是怎麼一個流程,廢話不多說,看下面的程式碼

 <input type="text" value="a" name="input" onkeydown="alert(this.value)" />
<input type="text" value="a" name="input" onkeydown="var me=this;setTimeout(function(){alert(me.value)},0)" >

  第一個keydown的時候,彈出來的是input裡原來的value,而第2個在keydown的時候,卻能彈出更新後的value,就是因為setTimeout,雖然他的delay設定為0,幾乎是即時觸發,但還是被新增到了執行佇列後面,但就是這個非同步過程,渲染已經完成了,當他回撥函式執行時,輸出來的已經是更新後的value了。這裡還有一個問題是this在不同的function的內外部所指示的不同物件的問題,當function裡還有function的時候 多多注意this,很容易出錯誤,細心就好,不多說。

  接下來講一下,js js是單執行緒的,可想而知,如果沒有多執行緒的話,整個程式就卡死了,幸好瀏覽器是多執行緒的,瀏覽器使得js具備非同步的一些屬性:js是單執行緒語言,瀏覽器只分配給js一個主執行緒,用來執行任務(函式),但一次只能執行一個任務,這些任務形成一個任務佇列排隊等候執行,但前端的某些任務是非常耗時的,比如網路請求,定時器和事件監聽,如果讓他們和別的任務一樣,都老老實實的排隊等待執行的話,執行效率會非常的低,甚至導致頁面的假死。所以,瀏覽器為這些耗時任務開闢了另外的執行緒,主要包括http請求執行緒,瀏覽器定時觸發器,瀏覽器事件觸發執行緒,這些任務是非同步的。

瀏覽器為網路請求這樣的非同步任務單獨開了一個執行緒,那麼問題來了,這些非同步任務完成後,主執行緒怎麼知道呢?答案就是回撥函式,整個程式是事件驅動的,每個事件都會繫結相應的回撥函式,舉個栗子,有段程式碼設定了一個定時器

setTimeout(function(){
    console.log(time is out);
},500);

執行這段程式碼的時候,瀏覽器非同步執行計時操作,當500ms到了後,會觸發定時事件,這個時候,就會把回撥函式放到任務佇列裡。整個程式就是通過這樣的一個個事件驅動起來的。
所以說,js是一直是單執行緒的,瀏覽器才是實現非同步的關鍵

以下轉自網際網路:

js一直在做一個工作,就是從任務佇列裡提取任務,放到主執行緒裡執行。下面我們來進行更深一步的理解。
event loop
圖片來自Philip Roberts的演講《Help, I'm stuck in an event-loop》非常深刻!
我們把剛才瞭解的概念和圖中做一個對應,上文中說到的瀏覽器為非同步任務單獨開闢的執行緒可以統一理解為WebAPIs,上文中說到的任務佇列就是callback queue,我們所說的主執行緒就是有虛線組成的那一部分,堆(heap)和棧(stack)共同組成了js主執行緒,函式的執行就是通過進棧和出棧實現的,比如圖中有一個foo()函式,主執行緒把它推入棧中,在執行函式體時,發現還需要執行上面的那幾個函式,所以又把這幾個函式推入棧中,等到函式執行完,就讓函數出棧。等到stack清空時,說明一個任務已經執行完了,這時就會從callback queue中尋找下一個人任務推入棧中(這個尋找的過程,叫做event loop,因為它總是迴圈的查詢任務佇列裡是否還有任務)。