提到異步,腦子里跳出來的第一個詞是ajax異步加載,異步即相互不影響,總感覺異步離我很遠,直到去看了相關(guān)的文檔,瞬間恍然大悟。
1. 單線程
首先了解下,JavaScript是采用的是單線程模型(JavaScript 同時只能執(zhí)行一個任務(wù),其他任務(wù)都必須在后面排隊等待)
2. 同步任務(wù)和異步任務(wù)
程序里面的所有任務(wù),簡單分成兩類:同步任務(wù)(synchronous)和異步任務(wù)(asynchronous)。
同步任務(wù)
是那些沒有被引擎掛起、在主線程上排隊執(zhí)行的任務(wù)。只有前一個任務(wù)執(zhí)行完畢,才能執(zhí)行后一個任務(wù)。
異步任務(wù)
是那些被引擎放在一邊,不進入主線程、而進入任務(wù)隊列的任務(wù)。只有引擎認為某個異步任務(wù)可以執(zhí)行了(比如 Ajax 操作從服務(wù)器得到了結(jié)果),該任務(wù)(采用回調(diào)函數(shù)的形式)才會進入主線程執(zhí)行。排在異步任務(wù)后面的代碼,不用等待異步任務(wù)結(jié)束會馬上運行,也就是說,異步任務(wù)不具有”堵塞“效應(yīng)。
(搬運過來的概念)
口語化,同步就是一個一個地執(zhí)行,后面的在排隊等待。異步就是一個在執(zhí)行,異步不在等待的隊伍中,當(dāng)達到了觸發(fā)異步的點,異步被調(diào)用執(zhí)行,異步執(zhí)行完了繼續(xù)按照原來的順序來執(zhí)行任務(wù)。
3. 任務(wù)隊列和事件循環(huán)
首先,主線程會去執(zhí)行所有的同步任務(wù)。等到同步任務(wù)全部執(zhí)行完,就會去看任務(wù)隊列里面的異步任務(wù)。如果滿足條件,那么異步任務(wù)就重新進入主線程開始執(zhí)行,這時它就變成同步任務(wù)了。等到執(zhí)行完,下一個異步任務(wù)再進入主線程開始執(zhí)行。一旦任務(wù)隊列清空,程序就結(jié)束執(zhí)行。
異步任務(wù)的寫法通常是回調(diào)函數(shù)。一旦異步任務(wù)重新進入主線程,就會執(zhí)行對應(yīng)的回調(diào)函數(shù)。如果一個異步任務(wù)沒有回調(diào)函數(shù),就不會進入任務(wù)隊列,也就是說,不會重新進入主線程,因為沒有用回調(diào)函數(shù)指定下一步的操作。
(概念有點暈)
4. 異步操作的幾種模式
4.1 回調(diào)函數(shù)
同步:
function f1() {
// ...
}
function f2() {
// ...
}
f1();
f2();
異步:
function f1(callback) {
// ...
callback();
}
function f2() {
// ...
}
f1(f2);
4.2 事件監(jiān)聽
異步的執(zhí)行不取決于代碼的順序,而取決于某個事件(觸發(fā)它的點)是否發(fā)生。
f1.on('done', f2);
function f1() {
setTimeout(function () {
// ...
f1.trigger('done');
}, 1000);
}
4.3 發(fā)布/訂閱
可以理解為事件監(jiān)聽的另一種形式。
優(yōu)勢:可以通過查看“消息中心”,了解存在多少信號、每個信號有多少訂閱者,從而監(jiān)控程序的運行。
jQuery.subscribe('done', f2); //訂閱
function f1() {
setTimeout(function () {
// ...
jQuery.publish('done'); //訂閱
}, 1000);
}
- 異步操作的流程控制
如果有多個異步操作,就存在一個流程控制的問題:如何確定異步操作執(zhí)行的順序,以及如何保證遵守這種順序。
5.1 串行執(zhí)行
var items = [ 1, 2, 3, 4, 5, 6 ];
var results = [];
function async(arg, callback) {
console.log('參數(shù)為 ' + arg +' , 1秒后返回結(jié)果');
setTimeout(function () { callback(arg * 2); }, 1000);
}
function final(value) {
console.log('完成: ', value);
}
function series(item) {
if(item) {
async( item, function(result) {
results.push(result);
return series(items.shift());
});
} else {
return final(results[results.length - 1]);
}
}
series(items.shift());
(一開始看了半個鐘沒get到點)
5.2 并行執(zhí)行
流程控制函數(shù)也可以是并行執(zhí)行,即所有異步任務(wù)同時執(zhí)行,等到全部完成以后,才執(zhí)行final函數(shù)。
var items = [ 1, 2, 3, 4, 5, 6 ];
var results = [];
function async(arg, callback) {
console.log('參數(shù)為 ' + arg +' , 1秒后返回結(jié)果');
setTimeout(function () { callback(arg * 2); }, 1000);
}
function final(value) {
console.log('完成: ', value);
}
items.forEach(function(item) {
async(item, function(result){
results.push(result);
if(results.length === items.length) {
final(results[results.length - 1]);
}
})
});
看完才知道,原來我日常寫的都是異步?。≈皇菦]有一個很清晰的概念,有回調(diào)函數(shù)的都是異步啦(這樣立即應(yīng)該沒錯吧)
5.3 并行與串行的結(jié)合
所謂并行與串行的結(jié)合,就是設(shè)置一個門檻,每次最多只能并行執(zhí)行n個異步任務(wù),這樣就避免了過分占用系統(tǒng)資源。
(看到這感覺可以忽略掉后面的代碼了)
var items = [ 1, 2, 3, 4, 5, 6 ];
var results = [];
var running = 0;
var limit = 2;
function async(arg, callback) {
console.log('參數(shù)為 ' + arg +' , 1秒后返回結(jié)果');
setTimeout(function () { callback(arg * 2); }, 1000);
}
function final(value) {
console.log('完成: ', value);
}
function launcher() {
while(running < limit && items.length > 0) {
var item = items.shift();
async(item, function(result) {
results.push(result);
running--;
if(items.length > 0) {
launcher();
} else if(running == 0) {
final(results);
}
});
running++;
}
}
launcher();
記錄此文,方便日后翻閱有關(guān)異步的相關(guān)知識點,大部分內(nèi)容來源于網(wǎng)絡(luò)。
傳送門:
https://wangdoc.com/javascript/async/general.html#%E5%BC%82%E6%AD%A5%E6%93%8D%E4%BD%9C%E7%9A%84%E6%B5%81%E7%A8%8B%E6%8E%A7%E5%88%B6