摘要:單線程與瀏覽器多線程是單線程的因為運(yùn)行在瀏覽器中,是單線程的,每個一個線程。若以多線程的方式操作這些,則可能出現(xiàn)操作的沖突。零延遲零延遲并不是意味著回調(diào)函數(shù)立刻執(zhí)行。異步編程的中方法包括回調(diào)函數(shù)事件監(jiān)聽采用事件驅(qū)動模式。
JavaScript單線程與瀏覽器多線程
Javascript是單線程的:因為JS運(yùn)行在瀏覽器中,是單線程的,每個window一個JS線程。作為瀏覽器腳本語言,JavaScript的主要用途是與用戶互動,以及操作DOM。若以多線程的方式操作這些DOM,則可能出現(xiàn)操作的沖突。假設(shè)有兩個線程同時操作一個DOM元素,線程1要求瀏覽器刪除DOM,而線程2卻要求修改DOM樣式,這時瀏覽器就無法決定采用哪個線程的操作。
瀏覽器不是單線程的,引擎可能存在如下線程:
javascript引擎線程
界面渲染線程
瀏覽器事件觸發(fā)線程
Http請求線程等
Event Loop事件循環(huán)機(jī)制:
主線程從"任務(wù)隊列"中讀取事件,這個過程是循環(huán)不斷的,所以整個的這種運(yùn)行機(jī)制又稱為Event Loop(事件循環(huán))。主線程運(yùn)行的時候,產(chǎn)生堆(heap)和棧(stack),棧中的代碼調(diào)用各種外部API,它們在"任務(wù)隊列"中加入各種事件(click,load,done)。只要棧中的代碼執(zhí)行完畢,主線程就會去讀取"任務(wù)隊列",依次執(zhí)行那些事件所對應(yīng)的回調(diào)函數(shù)。
執(zhí)行棧:所有同步任務(wù)都在主線程上執(zhí)行,形成一個執(zhí)行棧;
任務(wù)隊列:主線程之外,還存在一個"任務(wù)隊列"(task queue)。只要異步任務(wù)有了運(yùn)行結(jié)果,就在"任務(wù)隊列"之中放置一個事件,當(dāng)棧為空時,就會從任務(wù)隊列中取出一個任務(wù)并執(zhí)行。
定時器:
定時器包括setTimeout與setInterval兩個方法。它們的第二個參數(shù)是指定其回調(diào)函數(shù)推遲每隔多少毫秒數(shù)后執(zhí)行。
零延遲 setTimeout(func, 0):零延遲并不是意味著回調(diào)函數(shù)立刻執(zhí)行。它取決于主線程當(dāng)前是否空閑與“任務(wù)隊列”里其前面正在等待的任務(wù)。當(dāng)計時器時間規(guī)定的時候,回調(diào)函數(shù)會被添加到“任務(wù)隊列”中,等到執(zhí)行棧的任務(wù)執(zhí)行完畢,就會來執(zhí)行這里的任務(wù)。所以,有的時候即使計時器已經(jīng)到0了,也會不立即執(zhí)行計時器中的回調(diào)任務(wù)。
Ajax異步請求:
在發(fā)起ajax請求的時候,瀏覽器會開一個線程去執(zhí)行這一步驟,發(fā)請求這一步是屬于執(zhí)行棧中的同步任務(wù),在請求發(fā)完之后就會執(zhí)行棧中的下一個任務(wù),而等到請求有了結(jié)果,就會把結(jié)果放入“任務(wù)隊列”中,等待執(zhí)行;
JavaScript是單線程的,所謂“單線程”,就是同一時間只能執(zhí)行一個任務(wù)。如果有多個任務(wù),就必須排隊,直到前一個任務(wù)完成。這種模式的好處就是實現(xiàn)比較簡單,尤其在瀏覽器環(huán)境中,可以避免很多不必要的麻煩。壞處就是如果一個任務(wù)耗時很長,那么該任務(wù)后面的任務(wù)就必須一直等待,導(dǎo)致整個頁面都卡住無法繼續(xù)往下執(zhí)行。
為了解決這個問題,JavaScript將任務(wù)的執(zhí)行模式分為兩類,一個是同步模式,另一個是異步模式。
"同步模式"就是上一段的模式,后一個任務(wù)等待前一個任務(wù)結(jié)束,然后再執(zhí)行,程序的執(zhí)行順序與任務(wù)的排列順序是一致的、同步的;"異步模式"則完全不同,每一個任務(wù)有一個或多個回調(diào)函數(shù)(callback),前一個任務(wù)結(jié)束后,不是執(zhí)行后一個任務(wù),而是執(zhí)行回調(diào)函數(shù),后一個任務(wù)則是不等前一個任務(wù)結(jié)束就執(zhí)行,所以程序的執(zhí)行順序與任務(wù)的排列順序是不一致的、異步的。
JS異步編程的4中方法包括:
回調(diào)函數(shù):
f1(); f2(); function f1(callback){ setTimeout(function(){ callback(); }) } f1(f2);
事件監(jiān)聽:采用事件驅(qū)動模式。任務(wù)的執(zhí)行不取決于代碼的順序,而取決于某個事件是否發(fā)生。
//jQuery f1.on("done", f2); function f1(){ setTimeout(function () { //do something f1.trigger("done");//執(zhí)行完成后,立即觸發(fā)done事件,從而開始執(zhí)行f2。 }, 1000); }
發(fā)布/訂閱:又稱觀察者模式
jQuery.subscribe("done", f2);//訂閱"done"這個信號 function f1(){ setTimeout(function () { //do something jQuery.publish("done");//f1()執(zhí)行完之后,發(fā)出"done"這個信號,f2開始執(zhí)行 }, 1000); } jQuery.unsubscribe("done", f2);//取消訂閱
Promises:ES6原生提供了Promise對象。所謂Promise對象,就是代表了未來某個將要發(fā)生的事件(通常是一個異步操作)。它的好處在于,有了Promise對象,就可以將異步操作以同步操作的流程表達(dá)出來,避免了層層嵌套的回調(diào)函數(shù)。此外,Promise對象還提供了一整套完整的接口,使得可以更加容易地控制異步操作。
var promise = new Promise(function(resolve, reject) { if (/* 異步操作成功 */){ resolve(value); } else { reject(error); } }); promise.then(function(value) { // success }, function(value) { // failure });ES6模塊管理
ECMAScript 6基于export和import,定義了模塊的導(dǎo)出和導(dǎo)入規(guī)范,在語言標(biāo)準(zhǔn)層面實現(xiàn)了模塊機(jī)制。該標(biāo)準(zhǔn)的目標(biāo)是創(chuàng)建一種能夠兼容CommoneJS和AMD兩標(biāo)準(zhǔn)的規(guī)范,即可以像CommoneJS一樣語法簡潔、使用單一的接口且支持循環(huán)依賴,又可以像AMD支持異步加載和可配置的模塊加載。
大致來說,當(dāng) JS 引擎運(yùn)行一個模塊的時候,它的行為大致可歸納為以下四步:
解析:引擎實現(xiàn)會閱讀模塊的源碼,并且檢查是否有語法錯誤。
加載:引擎實現(xiàn)會(遞歸地)加載所有被引入的模塊。
鏈接:引擎實現(xiàn)會為每個新加載的模塊創(chuàng)建一個作用域,并且將模塊中的聲明綁定填入其中,包括從其他模塊中引入的。
JS加載動態(tài)加載常用的4種方法:
document.write:document.write("");
動態(tài)改變已有script的src屬性:js.src = "package.js";
動態(tài)創(chuàng)建script元素:
var script = document.createElement("script"); script.src = "XXX"; script.type = "XXX"; document.head.appendChild(script);
用XMLHTTP取得要腳本的內(nèi)容,再創(chuàng)建 Script 對象。
項目中加載JS的方法:
在頁面底部寫一個自調(diào)用函數(shù),加載入口JS,然后入口中的方法執(zhí)行,加載需要的其他JS。
在VM平臺上,可以選擇將幾個模塊分組打包到一個入口文件中,然后在這個入口文件中,將每個組動態(tài)生成一個script標(biāo)簽,插入head中。
異步加載:
瀏覽器在渲染一個頁面的時候,遇到一個