你們都知道,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,使得異步編程變得簡單。