淺談js異步

你們都知道,js是一個單線程的語言(只有一個線程來執行js函數),因此若是某一個函數執行任務耗時比較長的話,就會形成阻塞,使得後續任務一直處於等待狀態。html

1、阻塞示例ajax

        function f1(){
            for (var i = 0; i < 2000; i++) {
                    console.log('f1');
            }
        }
        function f2(){
            console.log('f2');
        }
        f1();
        f2();

f一、f2依次執行,控制檯打印:編程

2、setTimeout函數異步

        function f1(){
            for (var i = 0; i < 2000; i++) {
                setTimeout(()=>{
                    console.log('f1');
                },500);
            }
        }
        function f2(){
            console.log('f2');
        }
        f1();
        f2();

f一、f2依次執行,控制檯打印:異步編程

看上去好像先執行了f2,後續才執行了f1,其實否則,實際上依舊是先執行f1,再執行f2,沒有實現異步,f一、f2依然是同步的(不要質疑,我說的是f1,f2沒有實現異步)。函數

首先執行f1,利用for循環開啓2000個定時器;spa

而後執行f2,打印字符串;線程

時間到了,2000個定時器觸發。3d

注意:定時器到了時間觸發以後,也並非當即執行的,這要取決於js單線程內有沒有還有沒有函數正在運行,咱們來看一個例子:code

        function f8(){
            for (let i = 0; i < 2000; i++) {
                setTimeout(()=>{
                    console.log('f8'+i);
                },50);
            }
        }
        function f9(){
            for (let j = 0; j < 2000; j++) {
                    console.log('f9'+j);
            }
        }
        f8();
        f9();

咱們看到,定時器的時間參數值很小,50ms,依然是依次執行f八、f9;

能夠看到,雖然定時器先開啓,而且第一個定時器在50ms以後,理當執行,可是此時發現,f9這個函數一直在執行任務,佔據了js線程,因此這些定時器任務只好乖乖地等f9的循環走完,纔開始執行。

因此說js聲明的全部變量/函數都是同步的(ES6的Promise實例化以後能夠有一個當即執行代碼塊,可視爲同步任務);

可是咱們能夠經過各類方式開啓一個異步任務(例如setTimeout函數的callback,Promise對象的then(callback)回調);

3、同步隊列、異步隊列

由於js是單線程的,因此任務會在線程上依次逐個執行;

在主線程上的任務隊列,這些任務是同步的,他們會按前後依次執行;

另外還有一種任務隊列,暫且稱他們爲異步任務隊列,顧名思義,他們是異步任務,異步任務的特色是,必須等主線程上的任務沒有了以後,才依次進入主線程執行。

來個例子感覺一下:

ok,根據上圖,咱們能夠知道,上述代碼中,任務創建的順序爲:

同步任務:Promise實例化、f1的第一行打印、f1的for循環開啓1000個定時器、5個f2的字符串打印;

異步任務:Promise的then回調、1000個定時器回調。

再根據以前的任務執行順序分析,先不要看下面的執行結果,在心中想一下控制檯的打印結果。

關於上述所提到的同步隊列和異步隊列(只是一種形象的說法),詳情請戳下篇博客

4、異步實現

從上面咱們能夠發現,setTimeout(定時器)的回調是異步任務,這些個任務是直接放到異步任務隊列中等待的。

咱們熟知的ajax之因此是異步,徹底得益於XMLHttpRequest這個異步對象。

ES6爲方便異步編程,直接提供了一個異步對象Promise,使得異步編程變得簡單。

相關文章
相關標籤/搜索