摘要:回調(diào)函數(shù)指定了下一步操作。異步操作的流程控制參數(shù)為秒后返回結(jié)果上面代碼的函數(shù)是一個(gè)異步任務(wù),非常耗時(shí),每次執(zhí)行需要秒才能完成,然后再調(diào)用回調(diào)函數(shù)。
單線程模型
同步任務(wù)和異步任務(wù)
任務(wù)隊(duì)列和事件循環(huán)
異步操作的模式
回調(diào)函數(shù)
事件監(jiān)聽
發(fā)布/訂閱
異步操作的流程控制
串行執(zhí)行
并行執(zhí)行
并行與串行的結(jié)合
1.單線程模型
指的是js只在線程運(yùn)行,一個(gè)時(shí)間執(zhí)行一個(gè)任務(wù),其他任務(wù)排隊(duì)。事實(shí)上是一個(gè)運(yùn)行腳本的主線程加多個(gè)后臺(tái)配合的線程。
事件循環(huán)機(jī)制
Js單線程模式使得cpu空閑,io操作慢(Ajax請求網(wǎng)絡(luò)資源)。cpu不管io操作,掛起任務(wù),運(yùn)行排隊(duì)后面的任務(wù),等io完成再執(zhí)行的機(jī)制。
2.同步任務(wù)和異步任務(wù)
任務(wù)全部分為這兩類。
同步任務(wù)在主線程的任務(wù)按排隊(duì)順序執(zhí)行。
異步任務(wù)指的是被引擎掛到一邊,不在主線程而進(jìn)去任務(wù)隊(duì)列的任務(wù)。等到可以執(zhí)行了,該任務(wù)采用回調(diào)函數(shù)的方式進(jìn)入主線程。在他后面的任務(wù)不等他結(jié)束馬上執(zhí)行。
舉例來說,Ajax 如果是同步任務(wù),主線程就等著 Ajax 操作返回結(jié)果,再往下執(zhí)行;如果是異步任務(wù),主線程在發(fā)出 Ajax 請求以后,就直接往下執(zhí)行,等到 Ajax 操作有了結(jié)果,主線程再執(zhí)行對(duì)應(yīng)的回調(diào)函數(shù)。
3.任務(wù)隊(duì)列和事件循環(huán)
Js除了正在運(yùn)行的主線程,還有一個(gè)異步任務(wù)隊(duì)列,
主線程先完成全部同步任務(wù),在檢查異步任務(wù)是否可以執(zhí)行,可以的話安排進(jìn)主線程,此時(shí)就變成同步任務(wù)了,然后繼續(xù)檢查。直到任務(wù)隊(duì)列為空。
異步任務(wù)主要寫法是回調(diào)函數(shù),因?yàn)橛谢卣{(diào)函數(shù)才會(huì)進(jìn)入任務(wù)隊(duì)列,等重新進(jìn)入主線程,馬上執(zhí)行回調(diào)函數(shù)。回調(diào)函數(shù)指定了下一步操作。
JavaScript 引擎怎么知道異步任務(wù)有沒有結(jié)果,能不能進(jìn)入主線程呢?答案就是引擎在不停地檢查,一遍又一遍,只要同步任務(wù)執(zhí)行完了,引擎就會(huì)去檢查那些掛起來的異步任務(wù),是不是可以進(jìn)入主線程了。這種循環(huán)檢查的機(jī)制,就叫做事件循環(huán)(Event Loop)
4.異步操作的模式
4.1回調(diào)函數(shù)
下面是兩個(gè)函數(shù)f1和f2,編程的意圖是f2必須等到f1執(zhí)行完成,才能執(zhí)行。
function f1() {
// ...
}
function f2() {
// ...
}
f1();
f2();
上面代碼的問題在于,如果f1是異步操作,f2會(huì)立即執(zhí)行,不會(huì)等到f1結(jié)束再執(zhí)行。
這時(shí),可以考慮改寫f1,把f2寫成f1的回調(diào)函數(shù)。
function f1(callback) {
// ...
callback();
}
function f2() {
// ...
}
f1(f2);
易理解實(shí)現(xiàn)
不利于閱讀和維護(hù),高耦合。
4.2事件監(jiān)聽
另一種思路是采用事件驅(qū)動(dòng)模式。異步任務(wù)的執(zhí)行不取決于代碼的順序,而取決于某個(gè)事件是否發(fā)生。
f1.on("done", f2);
上面這行代碼的意思是,當(dāng)f1發(fā)生done事件,就執(zhí)行f2。然后,對(duì)f1進(jìn)行改寫:
function f1() {
setTimeout(function () {
// ... f1.trigger("done");
}, 1000);
}
可綁定多個(gè)事件,每個(gè)事件觸發(fā)多個(gè)回調(diào),易理解,去耦合。
4.3發(fā)布/訂閱
某個(gè)任務(wù)(異步)完成,發(fā)布信號(hào)。多個(gè)任務(wù)訂閱(回調(diào)),從而知道自己什么時(shí)候執(zhí)行。
首先,f2向信號(hào)中心jQuery訂閱done信號(hào)。
jQuery.subscribe("done", f2);
然后,f1進(jìn)行如下改寫。
function f1() {
setTimeout(function () {
// ... jQuery.publish("done");
}, 1000);
}
上面代碼中,jQuery.publish("done")的意思是,f1執(zhí)行完成后,向信號(hào)中心jQuery發(fā)布done信號(hào),從而引發(fā)f2的執(zhí)行。
f2完成執(zhí)行后,可以取消訂閱(unsubscribe)。
jQuery.unsubscribe("done", f2);
這種方法的性質(zhì)與“事件監(jiān)聽”類似,但是明顯優(yōu)于后者。因?yàn)榭梢酝ㄟ^查看“消息中心”,了解存在多少信號(hào)、每個(gè)信號(hào)有多少訂閱者,從而監(jiān)控程序的運(yùn)行。
5.異步操作的流程控制
function async(arg, callback) {
console.log("參數(shù)為 " + arg +" , 1秒后返回結(jié)果");
setTimeout(function () { callback(arg * 2); }, 1000);
}
上面代碼的async函數(shù)是一個(gè)異步任務(wù),非常耗時(shí),每次執(zhí)行需要1秒才能完成,然后再調(diào)用回調(diào)函數(shù)。
如果有六個(gè)這樣的異步任務(wù),需要全部完成后,才能執(zhí)行最后的final函數(shù)。請問應(yīng)該如何安排操作流程?
function final(value) {
console.log("完成: ", value);
}
async(1, function (value) {
async(2, function (value) {
async(3, function (value) { async(4, function (value) { async(5, function (value) { async(6, final); }); }); });
});
});
// 參數(shù)為 1 , 1秒后返回結(jié)果
// 參數(shù)為 2 , 1秒后返回結(jié)果
// 參數(shù)為 3 , 1秒后返回結(jié)果
// 參數(shù)為 4 , 1秒后返回結(jié)果
// 參數(shù)為 5 , 1秒后返回結(jié)果
// 參數(shù)為 6 , 1秒后返回結(jié)果
// 完成: 12
上面代碼中,六個(gè)回調(diào)函數(shù)的嵌套,不僅寫起來麻煩,容易出錯(cuò),而且難以維護(hù)。
5.1串行執(zhí)行
我們可以編寫一個(gè)流程控制函數(shù),讓它來控制異步任務(wù),一個(gè)任務(wù)完成以后,再執(zhí)行另一個(gè)。這就叫串行執(zhí)行。
我們可以編寫一個(gè)流程控制函數(shù),讓它來控制異步任務(wù),一個(gè)任務(wù)完成以后,再執(zhí)行另一個(gè)。這就叫串行執(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());
上面代碼中,函數(shù)series就是串行函數(shù),它會(huì)依次執(zhí)行異步任務(wù),所有任務(wù)都完成后,才會(huì)執(zhí)行final函數(shù)。items數(shù)組保存每一個(gè)異步任務(wù)的參數(shù),results數(shù)組保存每一個(gè)異步任務(wù)的運(yùn)行結(jié)果。
注意,上面的寫法需要六秒,才能完成整個(gè)腳本。
5.2并行執(zhí)行
流程控制函數(shù)也可以是并行執(zhí)行,即所有異步任務(wù)同時(shí)執(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]); }
})
});
上面代碼中,forEach方法會(huì)同時(shí)發(fā)起六個(gè)異步任務(wù),等到它們?nèi)客瓿梢院?,才?huì)執(zhí)行final函數(shù)。
相比而言,上面的寫法只要一秒,就能完成整個(gè)腳本。這就是說,并行執(zhí)行的效率較高,比起串行執(zhí)行一次只能執(zhí)行一個(gè)任務(wù),較為節(jié)約時(shí)間。但是問題在于如果并行的任務(wù)較多,很容易耗盡系統(tǒng)資源,拖慢運(yùn)行速度。因此有了第三種流程控制方式。
5.3并行與串行的結(jié)合
所謂并行與串行的結(jié)合,就是設(shè)置一個(gè)門檻,每次最多只能并行執(zhí)行n個(gè)異步任務(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();
上面代碼中,最多只能同時(shí)運(yùn)行兩個(gè)異步任務(wù)。變量running記錄當(dāng)前正在運(yùn)行的任務(wù)數(shù),只要低于門檻值,就再啟動(dòng)一個(gè)新的任務(wù),如果等于0,就表示所有任務(wù)都執(zhí)行完了,這時(shí)就執(zhí)行final函數(shù)。
這段代碼需要三秒完成整個(gè)腳本,處在串行執(zhí)行和并行執(zhí)行之間。通過調(diào)節(jié)limit變量,達(dá)到效率和資源的最佳平衡
文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請注明本文地址:http://systransis.cn/yun/105811.html
摘要:標(biāo)準(zhǔn)庫中的所有方法都提供非阻塞的異步版本,并接受回調(diào)函數(shù),某些方法還具有對(duì)應(yīng)的阻塞方法,其名稱以結(jié)尾。比較代碼阻塞方法同步執(zhí)行,非阻塞方法異步執(zhí)行。 阻塞與非阻塞概述 此概述介紹了Node.js中阻塞與非阻塞調(diào)用之間的區(qū)別,此概述將引用事件循環(huán)和libuv,但不需要事先了解這些主題,假設(shè)讀者對(duì)JavaScript語言和Node.js回調(diào)模式有基本的了解。 I/O主要指與libuv支持的...
摘要:即同步請求,瀏覽器需要等待服務(wù)器處理請求,導(dǎo)致了瀏覽器端的阻塞。這使得應(yīng)用程序更為迅捷地回應(yīng)用戶動(dòng)作,并避免了在網(wǎng)絡(luò)上發(fā)送那些沒有改變的信息。引擎在客戶端運(yùn)行,承擔(dān)了一部分本來由服務(wù)器承擔(dān)的工作,從而減少了大用戶量下的服務(wù)器負(fù)載。 前言 博主博客:Stillwater的博客知乎專欄:前端汪汪本文為作者原創(chuàng)轉(zhuǎn)載請注明出處: http://hiztx.top/2017/01/11/a......
摘要:需要注意的是,并不是的替代品,兩者各自有其適應(yīng)的場景。但為了方便交流,我們通常將獲取資源的一方稱為客戶端主要的工具是瀏覽器,而將派發(fā)資源的一方稱為服務(wù)端又稱為服務(wù)器。它可以幫助我們?yōu)橹蟾拍罴?xì)節(jié)的學(xué)習(xí)打下良好基礎(chǔ)。 再也不學(xué)AJAX了是一個(gè)與AJAX主題相關(guān)的文章系列,包含以下三個(gè)部分的內(nèi)容: AJAX概述:主要回答AJAX是什么這個(gè)問題; 使用AJAX:介紹如何通過JavaSc...
摘要:本文主要講述高可用方案,以及京東云數(shù)據(jù)庫的高可用實(shí)現(xiàn)。事務(wù)日志傳送事務(wù)日志傳送提供了數(shù)據(jù)庫級(jí)別的高可用性保護(hù)。擁有鏡像角色的伙伴稱為鏡像服務(wù)器,其數(shù)據(jù)庫副本為當(dāng)前的鏡像數(shù)據(jù)庫。 showImg(https://segmentfault.com/img/bVbtNqp?w=688&h=113); 數(shù)據(jù)庫的高可用是指在硬件、軟件故障發(fā)生時(shí),可以將業(yè)務(wù)從發(fā)生故障的數(shù)據(jù)庫節(jié)點(diǎn)遷移至備用節(jié)點(diǎn)。本...
閱讀 811·2023-04-25 22:57
閱讀 3061·2021-11-23 10:03
閱讀 623·2021-11-22 15:24
閱讀 3166·2021-11-02 14:47
閱讀 2910·2021-09-10 11:23
閱讀 3128·2021-09-06 15:00
閱讀 3950·2019-08-30 15:56
閱讀 3336·2019-08-30 15:52