標籤:style blog http io ar os java on 檔案
前言:雖然關於js非同步模型的文章已經是相當多,前端們對其也都是有各自的理解。本文的目的是梳理一下我對js非同步模型的理解,以及希望給其他前端在這方面提供一個參考。本文只講js非同步模型,下一篇文章再去扯promise/a標準,jquery的promise和deferred對象。本文主要說的是瀏覽器端的js非同步。下面羅列一下關於這方面的資料:JavaScript非同步編程:設計快速響應的網路應用(這本書是為數不多專門講js非同步書),別人寫的關於js非同步文章(js非同步編程1,js非同步編程2),瀏覽器內部原理(這篇文章非常長,我只看了前面的一部分)。還有一些內容雷同的文章我就不列舉了。
js的非同步和java的非同步是完全不一樣的。java的非同步是多個線程同時工作,用同步鎖的方式實現同步資源的訪問。而js的非同步是基於事件的,多個線程往可執行事件隊列裡塞可執行事件來實現非同步,這種方式保證了同一時刻只有一個線程在訪問同步資源。先(純手繪,略難看):
此處的事件可以理解為一個程式碼片段,其通常的形式是一個回呼函數。事件處理常式會不停的檢查事件隊列中是否有可執行檔事件,如果有就會執行該事件。這些可執行事件是由兩個線程產生的,時間軸程和I/O線程。setTimeout,setInterval函數會設定時間軸程在之後的某個時刻或多個時刻往事件隊列上塞事件。ajax,dom的事件註冊,worker的註冊,postMessage等,都會在I/O線程上註冊,讓其在某個行為觸發時,往事件隊列上扔事件。以下就舉個例子。
首先建立aysnc.html,代碼如下:
<html> <head>aysnc</head> <body>
<div id=‘domAysnc‘>test</div> <script type="text/javascript" src=‘aysnc.js‘></script> </body></html>
aysnc.js的代碼如下:
var dom=document.getElementById(‘domAysnc‘);dom.onclick=function(){ console.log(‘I/O event‘);};window.setTimeout(function(){ console.log(‘time event‘);},0);
訪問aysnc.html這個資源,瀏覽器會載入aysnc.js這個檔案並交給js引擎解釋並執行。當執行dom.onclick=....這條語句時,I/O線程會被註冊一個事件,當dom被點擊時就會觸發I/O線程往事件隊列上塞入這個事件。這個觸發的過程和調用dom.click()是不同,是非同步和同步的區別。當執行window.setTimeout...這條語句時,時間軸程會被註冊一個事件,0這個實參表示立即往事件隊列上塞事件,實際上時間軸程產生一個事件(以及事件處理常式的輪詢)是需要時間的,最快的引擎大概是6ms左右。
下面來說一下window.setTimeout的一些應用。首先是動畫,在非現代瀏覽器下(包括ie9),動畫都是用window.setTimeout或window.setInterval來實現,把大的變化切分成一個個小的變化,然後用一個個很小的時間(在jquery中,這個很小的時間是13ms。)去執行這些小變化。這裡面就會讓js引擎不停的產生、執行事件,然後更新UI。而在現代瀏覽器下被支援的transition,可以指定某個屬性的過渡效果。他並不牽涉js的執行,所以效率會高很多。
window.setTimeout同樣可以用於大任務的拆分。比如在js上處理很大的一個數組,如果放在一個事件裡處理,是會阻塞事件處理常式的。window.setTimeout(function(){\*拆分成的小任務*\},2);,用類似的做法,如果別的事件被觸發,就可以被執行。
js非同步模型