1. 程式人生 > >JavaScript深入淺出非同步程式設計一、setTimeout和setInterval

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中,setTimeoutsetInterval可以實現類似非同步的效果。

不同的語言對於定時器的實現方式有可能不一樣,甚至同一種語言,提供多種不同的定時器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程式碼都是在同一個執行緒上執行的,因此不存在多執行緒的概念,也就不存在真正的非同步程式設計

其實某些瀏覽器支援workerAPI,算是真的多執行緒。但是本篇不做介紹。

既然本篇的主題是非同步程式設計,那麼就算是假象,也得繼續了。

XMLHttpRequest

我記得JS的非同步程式設計的概念被提出來是從XMLHttpRequest(Ajax)開始的,而這裡的非同步指的只是實際的http請求是在非JS執行緒執行的,而請求完畢後的回撥還是在JS執行緒執行的。這樣看起來是非同步的,但是我們要注意到,http非同步請求不是在JS執行緒上執行的,而是由瀏覽器負責的,跟JS沒關係。但是,正是因為有了這樣的模型,才使得一些原本無法實現的功能變得可能。