小試JavaScript多線程
更新時間:2025年03月02日 21:42:09 投稿:mdxy-dxy
這兩天一直在弄ajax,用多了才發(fā)現(xiàn)了ajax?的cache問題,請求了好多次,得到了確是相同的結(jié)果,經(jīng)常我想在請求的同時去做一些其它的事情,我在想javascript里有沒有辦法用多線程,經(jīng)過在網(wǎng)上查找找到了結(jié)果。
這個程序也在內(nèi)部調(diào)用了getArticle()函數(shù)。然而需要注意的是,為異步通信設計的這版getArticle()函數(shù)要接收一個函數(shù)作為第二個參數(shù)。當調(diào)用這個getArticle()函數(shù)時,與從前一樣要給服務器發(fā)送一個請求,不同的是,現(xiàn)在函數(shù)會迅速返回而非等待服務器的響應。這意味著,當執(zhí)行權(quán)交回給調(diào)用程序時,還沒有得到服務器的響應。如此一來,線程就可以去執(zhí)行其它任務直至獲得服務器響應,并在此時調(diào)用回調(diào)函數(shù)。一旦得到服務器響應, getArticle()的第二個參數(shù)作為預先定義的回調(diào)函數(shù)就要被調(diào)用,服務器的返回值即為其參數(shù)。同樣的,getArticleWithCache ()也要做些改變,定義一個回調(diào)參數(shù)作為其第二個參數(shù)。這個回調(diào)函數(shù)將在被傳給getArticle()的回調(diào)函數(shù)中調(diào)用,因而它可以在服務器通信結(jié)束后被執(zhí)行。
單是上面這些改動你可能已經(jīng)認為相當復雜了,但是對backgroundLoad()函數(shù)做得改動將會更復雜,它也要被改寫成可以處理回調(diào)函數(shù)的形式:
function backgroundLoad ( ids, callback ) {
var i = 0;
function l ( ) {
if ( i < ids.length ) {
getArticleWithCache(ids[i++], function( a ){
backgroundLoad(a.children, l);
});
} else {
callback();
}
}
l();
}
改動后的backgroundLoad()函數(shù)看上去和我們以前的那個函數(shù)已經(jīng)相去甚遠,不過他們所實現(xiàn)的功能并無二致。這意味著這兩個函數(shù)都接受 ID數(shù)組作為參數(shù),對于數(shù)組里的每個元素都要調(diào)用getArticleWithCache(),再應用已經(jīng)獲得子文章ID遞歸調(diào)用 backgroundLoad ()。不過同樣是對數(shù)組的循環(huán)訪問,新函數(shù)中的就不太好辨認了,以前的程序中是用一個for循環(huán)語句完成的。為什么實現(xiàn)同樣功能的兩套函數(shù)是如此的大相徑庭呢?
這個差異源于一個事實:任何函數(shù)在遇到有需要同服務器進行通信情況后,都必須立刻返回,例如getArticleWithCache()。除非原來的函數(shù)不在執(zhí)行當中,否則應當接受服務器響應的回調(diào)函數(shù)都不能被調(diào)用。對于JavaScript,在循環(huán)過程中中斷程序并在稍后從這個斷點繼續(xù)開始執(zhí)行程序是不可能的,例如一個for語句。因此,本例利用遞歸傳遞回調(diào)函數(shù)實現(xiàn)循環(huán)結(jié)構(gòu)而非一個傳統(tǒng)循環(huán)語句。對那些熟悉連續(xù)傳送風格(CPS)的人來說,這就是一個 CPS的手動實現(xiàn),因為不能使用循環(huán)語法,所以即便如前面提到的遍歷樹那么簡單的程序也得寫得很復雜。與事件驅(qū)動程序設計相關(guān)的問題是控制流問題:循環(huán)和其它控制流表達式可能比較難理解。
這里還有另外一個問題:如果你把一個沒有應用異步通信的函數(shù)轉(zhuǎn)換為一個使用異步通信的函數(shù),那么重寫的函數(shù)將需要一個回調(diào)函數(shù)作為新增參數(shù),這為已經(jīng)存在的APIs造成了很大問題,因為內(nèi)在的改變沒有把影響限于內(nèi)部,而是導致整體混亂的APIs以及API的其它使用者的改變。
造成這些問題目的根本原因是什么呢?沒錯,正是JavaScript單線程機制導致了這些問題。在單線程里執(zhí)行異步通信需要事件驅(qū)動程序設計和復雜的語句。如果當程序在等待服務器的響應時,有另外一個線程可以來處理用戶請求,那么上述復雜技術(shù)就不需要了。
試試多線程編程
讓我來介紹一下Concurrent.Thread,它是一個允許JavaScript進行多線程編程的庫,應用它可以大大緩解上文提及的在 AJAX開發(fā)中與異步通信相關(guān)的困難。這是一個用JavaScript寫成的免費的軟件庫,使用它的前提是遵守Mozilla Public License和GNU General Public License這兩個協(xié)議。你可以從他們的網(wǎng)站 下載源代碼。
馬上來下載和使用源碼吧!假定你已經(jīng)將下載的源碼保存到一個名為Concurrent.Thread.js的文件夾里,在進行任何操作之前,先運行如下程序,這是一個很簡單的功能實現(xiàn):
<script type="text/javascript" src="Concurrent.Thread.js"> </script>
<script type="text/javascript">
Concurrent.Thread.create(function(){
var i = 0;
while ( 1 ) {
document.body.innerHTML += i++ + " <br>";
}
});
</script>
執(zhí)行這個程序?qū)樞蝻@示從0開始的數(shù)字,它們一個接一個出現(xiàn),你可以滾屏來看它?,F(xiàn)在讓我們來仔細研究一下代碼,他應用while(1)條件制造了一個不會中止的循環(huán),通常情況下,象這樣不斷使用一個并且是唯一一個線程的JavaScript程序會導致瀏覽器看起來象凍結(jié)了一樣,自然也就不會允許你滾屏。那么為什么上面的這段程序允許你這么做呢?關(guān)鍵之處在于while(1)上面的那條Concurrent.Thread.create()語句,這是這個庫提供的一個方法,它可以創(chuàng)建一個新線程。被當做參數(shù)傳入的函數(shù)在這個新線程里執(zhí)行,讓我們對程序做如下微調(diào):
<script type="text/javascript" src="Concurrent.Thread.js"> </script>
<script type="text/javascript">
function f ( i ){
while ( 1 ) {
document.body.innerHTML += i++ + " <br>";
}
}
Concurrent.Thread.create(f, 0);
Concurrent.Thread.create(f, 100000);
</script>
在這個程序里有個新函數(shù)f()可以重復顯示數(shù)字,它是在程序段起始定義的,接著以f()為參數(shù)調(diào)用了兩次create()方法,傳給create()方法的第二個參數(shù)將會不加修改地傳給f()。執(zhí)行這個程序,先會看到一些從0開始的小數(shù),接著是一些從100,000開始的大數(shù),然后又是接著前面小數(shù)順序的數(shù)字。你可以觀察到程序在交替顯示小數(shù)和大數(shù),這說明兩個線程在同時運行。
讓我來展示Concurrent.Thread的另外一個用法。上面的例子調(diào)用create()方法來創(chuàng)建新線程。不調(diào)用庫里的任何APIs也有可能實現(xiàn)這個目的。例如,前面那個例子可以這樣寫:
<script type="text/javascript" src="Concurrent.Thread.js"> </script>
<script type="text/x-script.multithreaded-js">
var i = 1;
while ( 1 ) {
document.body.innerHTML += i++ + " <br>";
}
</script>
在script 標簽內(nèi),很簡單地用JavaScript寫了一個無窮循環(huán)。你應該注意到標簽內(nèi)的type屬性,那里是一個很陌生的值(text/x- script.multithreaded-js),如果這個屬性被放在script標簽內(nèi),那么Concurrent.Thread就會在一個新的線程內(nèi)執(zhí)行標簽之間的程序。你應當記住一點,在本例一樣,必須將Concurrent.Thread庫包含進來。
有了Concurrent.Thread,就有可能自如的將執(zhí)行環(huán)境在線程之間進行切換,即使你的程序很長、連續(xù)性很強。我們可以簡要地討論下如何執(zhí)行這種操作。簡言之,需要進行代碼轉(zhuǎn)換。粗略地講,首先要把傳遞給create()的函數(shù)轉(zhuǎn)換成一個字符串,接著改寫直至它可以被分批分次執(zhí)行。然后這些程序可以依照調(diào)度程序逐步執(zhí)行。調(diào)度程序負責協(xié)調(diào)多線程,換句話說,它可以在適當?shù)臅r候做出調(diào)整以便每一個修改后的函數(shù)都會得到同等機會運行。 Concurrent.Thread實際上并沒有創(chuàng)建新的線程,僅僅是在原本單線程的基礎上模擬了一個多線程環(huán)境。
雖然轉(zhuǎn)換后的函數(shù)看起來是運行在不同的線程內(nèi),但是實際上只有一個線程在做這所有的事情。在轉(zhuǎn)換后的函數(shù)內(nèi)執(zhí)行同步通信仍然會造成瀏覽器凍結(jié),你也許會認為以前的那些問題根本就沒有解決。不過你不必耽心,Concurrent.Thread提供了一個應用JavaScript 的異步通信方式實現(xiàn)的定制通信庫,它被設計成當一個線程在等待服務器的響應時允許其它線程運行。這個通信庫存于 Concurrent.Thread.Http下。它的用法如下所示:
<script type="text/javascript" src="Concurrent.Thread.js"> </script>
<script type="text/x-script.multithreaded-js">
var req = Concurrent.Thread.Http.get(url, ["Accept", "*"]);
if (req.status == 200) {
alert(req.responseText);
} else {
alert(req.statusText);
}
</script>
get()方法,就像它的名字暗示的那樣,可以通過HTTP的GET方法獲得指定URL的內(nèi)容,它將目標URL作為第一個參數(shù),將一個代表HTTP 請求頭的數(shù)組作為可選的第二個參數(shù)。get()方法與服務器交互,當?shù)玫椒掌鞯捻憫缶头祷匾粋€XMLHttpRequest對象作為返回值。當 get()方法返回時,已經(jīng)收到了服務器響應,所以就沒必要再用回調(diào)函數(shù)接收結(jié)果。自然,也不必再耽心當程序等待服務器的響應時瀏覽器凍結(jié)的情況了。另外,還有一個 post()方法可以用來發(fā)送數(shù)據(jù)到服務器:
<script type="text/javascript" src="Concurrent.Thread.js"> </script>
<script type="text/x-script.multithreaded-js">
var req = Concurrent.Thread.Http.post(url, "key1=val1&key2=val2");
alert(req.statusText);
</script>
post()方法將目的URL作為第一個參數(shù),要發(fā)送的內(nèi)容作為第二個參數(shù)。像get()方法那樣,你也可以將請求頭作為可選的第三個參數(shù)。
如果你用這個通信庫實現(xiàn)了第一個例子當中的getArticle()方法,那么你很快就能應用文章開頭示例的那種簡單的方法寫出 getArticleWithCache(),backgroundLoad ()以及其它調(diào)用了getArticle()方法的函數(shù)了。即使是那版backgroundLoad()正在讀文章數(shù)據(jù),照例還有另外一個線程可以對用戶請求做出響應,瀏覽器因此也不會凍結(jié)?,F(xiàn)在,你能理解在JavaScript中應用多線程有多實用了?
想了解更多
我向你介紹了一個可以在JavaScript中應用多線程的庫:Concurrent.Thread。這篇文章的內(nèi)容只是很初級的東西,如果你想更深入的了解,我推薦您去看the tutorial。它提供有關(guān)Concurrent.Thread用法的更多內(nèi)容,并列出了可供高級用戶使用的文檔,是最適合起步的材料。訪問他們的網(wǎng)站也不錯,那里提供更多信息。
有關(guān)作者
Daisuke Maki:從 International Christian大學文科學院自然科學分部畢業(yè)后(取得文學學士學位),又在Electro-Communications大學的研究生院信息專業(yè)攻讀碩士學位。擅長Web開發(fā)和應用JavaScript的AJAX。他開發(fā)了Concurrent.Thread。2006財政年度在日本信息技術(shù)促進機構(gòu)(IPA)指導的項目Explatory Software Project中應用了這個設計。
目前已經(jīng)擁有一個工學碩士學位的他正在Electro-Communications大學的研究生院注冊攻讀博士學位。
下載地址//www.dbjr.com.cn/jiaoben/8626.html
相關(guān)文章
為什么說JavaScript預解釋是一種毫無節(jié)操的機制詳析
這篇文章主要給大家介紹了關(guān)于為什么說JavaScript預解釋是一種毫無節(jié)操的機制的相關(guān)資料,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面來一起看看吧2018-11-11