js最大的特點就是單線程,也就是說同一時間只能做一件事。原因在于js主要用途是與用戶互動,操作DOM。假如是多線程,一個線程想刪除DOM,而另一個線程想在這個DOM上增加內(nèi)容,這樣的話就會矛盾。所以注定是單線程。
為了提高多核CPU的計算能力,HTML5提出Web Worker標準,允許js腳本創(chuàng)建多個線程,但是子線程完全受主線程控制,且不得操作DOM。所以,這個新標準并沒有改變js單線程的本質(zhì)。
既然js是單線程,那么不管有多少任務,都要排隊,只有一個任務完成,下一個任務才能執(zhí)行。如果任務耗時很長,那么就要一直等待。但很多時候CPU是閑著的,例如AJAX獲取數(shù)據(jù)是很慢的,只能一直等到數(shù)據(jù)返回才能執(zhí)行下個任務。
所以js設計者意識到這時完全可以把等待中的任務掛起,先執(zhí)行后續(xù)任務,等返回結果后再執(zhí)行該任務。
于是,把任務分為同步任務和異步任務。同步任務指在主線程排隊的任務,只有一個執(zhí)行完后才會執(zhí)行下一個任務。異步任務指的是,不進入主線程,而是進入任務隊列的任務,只有當任務隊列通知主線程,某個異步任務可執(zhí)行的時候,主線程才會執(zhí)行這個任務。
任務的運行機制:
(1)所有同步任務都在主線程上,形成一個執(zhí)行棧;
(2)除主線程外,還有一個任務隊列。只要異步任務執(zhí)行有了結果,會在任務隊列中放一個事件;
(3)當主線程上的所有同步任務執(zhí)行完成后,會去讀取任務隊列,看里面有哪些事件,那些對應的異步任務,結束等待狀態(tài),進入主線程被執(zhí)行。
(4)主線程不斷重復以上三步。
不得不說一下定時器的使用,可以設置一段時間后再執(zhí)行事件。
如果將第二個參數(shù),也就是推遲執(zhí)行的毫秒數(shù),設置為0時,表示當前代碼執(zhí)行完后,立即執(zhí)行回調(diào)函數(shù),而這里的“當前代碼執(zhí)行完”,指的是執(zhí)行棧中的所有任務執(zhí)行完成。
setTimeout(fn,0)的含義是,指定某個任務在主線程最早可得的空閑時間執(zhí)行,也就是說,盡可能早得執(zhí)行。它在任務隊列的尾部添加一個事件,因此要等到同步任務和任務隊列現(xiàn)有的事件都處理完,才會得到執(zhí)行。
HTML5標準規(guī)定了setTimeout()的第二個參數(shù)的最小值(最短間隔),不得低于4毫秒,如果低于這個值,就會自動增加。使用setTimeout()只是將事件插入了任務隊列,并不能保證在推遲執(zhí)行的毫秒數(shù)后立即執(zhí)行,因為前面的任務有可能耗時很長。