JavaScript深入淺出非同步程式設計一、setTimeout和setInterval
最近開發了一個適用於iOS上的混合開發庫,支援JavaScript
的開發,開發完以後對於JavaScript
中的一些特性有了更加深入的瞭解。也就有了這篇文章,後續還會陸續寫一些其他的關於JavaScript
的文章。
非同步程式設計
一般跟多執行緒
有關,而我們都知道JavaScript
是單執行緒執行的,那何來非同步
之說?
事實上,但凡在
JS
中執行的程式碼都是單執行緒執行的。
然而,我們平常開發JavaScript
的時候經常會提到非同步程式設計
的概念,而且在實際的開發過程中也經常會用到非同步程式設計
。那先說下我們是怎麼用的,從最簡單的setTimeout
說起,然後一步一步的說到ES8中的async
await
。
setTimeout、setInterval
setTimeout(function(){
console.log('timeout');
},1000);
複製程式碼
執行上面的程式碼1秒後輸出timeout
。下面換一種方式,使用setInterval
來實現。
var interval = setInterval(function () {
console.log('timeout');
clearInterval(interval);
}, 1000);
複製程式碼
上面兩種方式實現的效果是一樣的,都是在等待1秒後執行function
中的程式碼。
看到上面的程式碼,但凡寫過其他語言的比如:c++、java、OC等的,都知道這其實就是一個定時器,而setTimeout
setInterval
能夠執行多次,直到顯示取消為止。
舉上面的例子主要是說明,在JavaScript中,setTimeout
和setInterval
可以實現類似非同步的效果。
不同的語言對於定時器的實現方式有可能不一樣,甚至同一種語言,提供多種不同的定時器API,就拿
Objectiv-c
來說,起碼有三種方式實現定時器的效果,但是不管什麼語言,用什麼方式來實現,工作原理差不多,
就是當
定時器
獲取到某個訊號
的時候,在建立該定時器的執行緒中插入任務。這樣的過程可以理解為執行緒排程。因為定時訊號
不一定在你回撥的執行緒上觸發的,該訊號
有可能直接由硬體中斷
產生,也有可能是軟體模擬
產生,但是在使用的時候都會在建立的執行緒獲得回撥。
因此,定時器本質上還是單執行緒的,跟非同步程式設計
沒關係。我舉個例子你就明白了
var t1 = new Date();
setTimeout(function(){
var t2 = new Date();
console.log('diff:'+(t2-t1));
},1000);
while (new Date() - t1 < 2000) {
}
複製程式碼
上面的程式碼你覺得是輸出1000
呢?還是2000
?
實際的程式碼輸出是diff:2000
,具體是2000多少要看實際情況。舉這樣的例子說明什麼呢,正是說明setTimeout
還是序列執行的,你可以理解為見縫插針
方式。因此setTimeout
壓根就算不上非同步
。
一個殘酷的現實是,javascript
中不存在真的非同步
,一切非同步
都是假象。
所有的JS程式碼都是在同一個執行緒上執行的,因此不存在多執行緒的概念,也就不存在真正的非同步程式設計。
其實某些瀏覽器支援
worker
API,算是真的多執行緒。但是本篇不做介紹。
既然本篇的主題是非同步程式設計
,那麼就算是假象,也得繼續了。
XMLHttpRequest
我記得JS的非同步程式設計的概念被提出來是從XMLHttpRequest
(Ajax)開始的,而這裡的非同步指的只是實際的http請求
是在非JS執行緒執行的,而請求完畢
後的回撥還是在JS執行緒執行的。這樣看起來是非同步的,但是我們要注意到,http非同步請求
不是在JS執行緒上執行的,而是由瀏覽器負責的,跟JS沒關係。但是,正是因為有了這樣的模型,才使得一些原本無法實現的功能變得可能。