先看一個簡單的事實:

復(fù)制代碼 代碼如下:<SCRIPT LANGUAGE="JavaScript"> var img=new Image(); img.src="attachment/117836529 " /> 国精品产露脸偷拍视频,高肉黄暴NP文公交车,beeg日本老师按摩

天天躁日日躁狠狠躁AV麻豆-天天躁人人躁人人躁狂躁-天天澡夜夜澡人人澡-天天影视香色欲综合网-国产成人女人在线视频观看-国产成人女人视频在线观看

JavaScript多線程的實現(xiàn)方法

注:以下內(nèi)容基于IE中GIF的onload事件的基礎(chǔ)上,故所有測試IE only
需要用到的幾個圖片

先看一個簡單的事實:

復(fù)制代碼 代碼如下:
<SCRIPT LANGUAGE="JavaScript">
var img=new Image();
img.src="attachment/1178365293_0.gif";
img.onload=function()
{
alert("如要關(guān)閉請按住ESC鍵不放,并點擊關(guān)閉按鈕");
}
</SCRIPT>


如果不出所料的話你的IE應(yīng)該彈出一大堆alert提示窗口了。注意是“一大堆”!

原因很簡單:IE對多幀GIF的onload事件重復(fù)執(zhí)行,即每播放完一次動畫,就重新執(zhí)行一次onload事件。

(注:按下ESC鍵會停止gif動畫的播放,故也會停止onload事件執(zhí)行)

利用這一特性我們可以模擬多線程的實現(xiàn):
看下面的代碼:

復(fù)制代碼 代碼如下:
image A onload執(zhí)行次數(shù):<span id="ThreadA">0</span><br>
image B onload執(zhí)行次數(shù):<span id="ThreadB">0</span><br>
image C onload執(zhí)行次數(shù):<span id="ThreadC">0</span>
<script>
function Img(threadID,src)
{
var img = new Image;
img.onload = function()
{
var c = parseInt(document.getElementById(threadID).innerHTML);
document.getElementById(threadID).innerHTML=isNaN(c)?1:++c;
}
img.src = src;
return img;
}

var imgA = new Img("ThreadA","attachment/1178365293_0.gif");
var imgB = new Img("ThreadB","attachment/1178365293_1.gif");
var imgC = new Img("ThreadC","attachment/1178365293_2.gif");
</script>


是不是有點意思了?
再看下面的代碼:

復(fù)制代碼 代碼如下:
<script>
//by Rimifon
var Threads = new Array;
onload = function()
{
for(var C=1;C<501;C++)
{
Threads.push(new Thread(C));
}
}
function Go(sender)
{
var IsStart = sender.value=="全部開始";
for(var x in Threads)
{
Threads[x].Start = IsStart;
}
sender.value = "全部" + (IsStart?"暫停":"開始");
}
function Thread(ID)
{
this.Start = 0;
var cursor = this;
var span = document.createElement("span");
var counter = document.createTextNode("0");
span.appendChild(counter);
var div = document.createElement("div");
div.appendChild(document.createTextNode("線程" + ID + ":"));
div.style.cursor = "pointer";
div.onclick = function()
{
cursor.Start = !cursor.Start;
}
div.oncontextmenu = function()
{
img.onload = null;
this.removeNode(true);
return false;
}
div.appendChild(span);
document.body.appendChild(div);
var img = new Image;
img.onload = function()
{
if(cursor.Start) counter.data = parseInt(counter.data) + 1;
div.style.backgroundColor = cursor.Start?"#abcdef":"yellow";
}
img.src = "images/51js.gif";
}
</script>
<input type=button value="全部開始" onclick="Go(this)">
<input type=button value="彈出對話框" onclick="alert('對話框測試')">

部分代碼引自Rimifon。(轉(zhuǎn)載 http://Dnew.cn) 
JavaScript多線程編程簡介
雖然有越來越多的網(wǎng)站在應(yīng)用AJAX技術(shù)進行開發(fā),但是構(gòu)建一個復(fù)雜的AJAX應(yīng)用仍然是一個難題。造成這些困難的主要原因是什么呢?是與服務(wù)器的異步通信問題?還是GUI程序設(shè)計問題呢?通常這兩項工作都是由桌面程序來完成的,那究竟為何開發(fā)一個可以實現(xiàn)同樣功能的AJAX應(yīng)用就這么困難呢?

AJAX 開發(fā)中的難題
讓我們通過一個簡單的例子來認識這個問題。假設(shè)你要建立一個樹形結(jié)構(gòu)的公告欄系統(tǒng)(BBS),它可以根據(jù)用戶請求與服務(wù)器進行交互,動態(tài)加載每篇文章的信息,而不是一次性從服務(wù)器載入所有文章信息。每篇文章有四個相關(guān)屬性:系統(tǒng)中可以作為唯一標識的ID、發(fā)貼人姓名、文章內(nèi)容以及包含其所有子文章ID的數(shù)組信息。首先假定有一個名為getArticle()的函數(shù)可以加載一篇文章信息。該函數(shù)接收的參數(shù)是要加載文章的ID,通過它可從服務(wù)器獲取文章信息。它返回的對象包含與文章相關(guān)的四條屬性:id,name,content和children。例程如下:


復(fù)制代碼 代碼如下:
function ( id ) {
var a = getArticle(id);
document.writeln(a.name + "" + a.content);
}

然而你也許會注意到,重復(fù)用同一個文章ID調(diào)用此函數(shù),需要與服務(wù)器之間進行反復(fù)且無益的通信。想要解決這個問題,可以考慮使用函數(shù) getArticleWithCache(),它相當(dāng)于一個帶有緩存能力的getArticle()。在這個例子中,getArticle()返回的數(shù)據(jù)只是作為一個全局變量被保存下來:
復(fù)制代碼 代碼如下:
var cache = {};
function getArticleWithCache ( id ) {
if ( !cache[id] ) {
cache[id] = getArticle(id);
}
return cache[id];
}

現(xiàn)在已將讀入的文章緩存起來,讓我們再來考慮一下函數(shù)backgroundLoad(),它應(yīng)用我們上面提到的緩存機制加載所有文章信息。其用途是,當(dāng)讀者在閱讀某篇文章時,從后臺預(yù)加載它所有子文章。因為文章數(shù)據(jù)是樹狀結(jié)構(gòu)的,所以很容易寫一個遞歸的算法來遍歷樹并且加載所有的文章:
復(fù)制代碼 代碼如下:
function backgroundLoad ( ids ) {
for ( var i=0; i < ids.length; i++ ) {
var a = getArticleWithCache(ids[i]);
backgroundLoad(a.children);
}
}

backgroundLoad ()函數(shù)接收一個ID數(shù)組作為參數(shù),然后通過每個ID調(diào)用前面定義過的getArticldWithCache()方法,這樣就把每個ID對應(yīng)的文章緩存起來。之后再通過已加載文章的子文章ID數(shù)組遞歸調(diào)用backgroundLoad()方法,如此整個文章樹就被緩存起來。

到目前為止,一切似乎看起來都很完美。然而,只要你有過開發(fā)AJAX應(yīng)用的經(jīng)驗,你就應(yīng)該知曉這種幼稚的實現(xiàn)方法根本不會成功,這個例子成立的基礎(chǔ)是默認 getArticle()用的是同步通信。可是,作為一條基本原則,JavaScript要求在與服務(wù)器進行交互時要用異步通信,因為它是單線程的。就簡單性而言,把每一件事情(包括GUI事件和渲染)都放在一個線程里來處理是一個很好的程序模型,因為這樣就無需再考慮線程同步這些復(fù)雜問題。另一方面,他也暴露了應(yīng)用開發(fā)中的一個嚴重問題,單線程環(huán)境看起來對用戶請求響應(yīng)迅速,但是當(dāng)線程忙于處理其它事情時(比如說調(diào)用getArticle()),就不能對用戶的鼠標點擊和鍵盤操作做出響應(yīng)。

如果在這個單線程環(huán)境里進行同步通信會發(fā)生什么事情呢?同步通信會中斷瀏覽器的執(zhí)行直至獲得通信結(jié)果。在等待通信結(jié)果的過程中,由于服務(wù)器的調(diào)用還沒有完成,線程會停止響應(yīng)用戶并保持鎖定狀態(tài)直到調(diào)用返回。因為這個原因,當(dāng)瀏覽器在等待服務(wù)器響應(yīng)時它不能對用戶行為作出響應(yīng),所以看起來像是凍結(jié)了。當(dāng)執(zhí)行 getArticleWithCache()和backgroundLoad()會有同樣的問題,因為它們都是基于getArticle()函數(shù)的。由于下載所有的文章可能會耗費很可觀的一段時間,因此對于backgroundLoad()函數(shù)來說,瀏覽器在此段時間內(nèi)的凍結(jié)就是一個很嚴重的問題――既然瀏覽器都已經(jīng)凍結(jié),當(dāng)用戶正在閱讀文章時就不可能首先去執(zhí)行后臺預(yù)加載數(shù)據(jù),如果這樣做連當(dāng)前的文章都沒辦法讀。

如上所述,既然同步通信在使用中會造成如此嚴重的問題,JavaScript就把異步通信作為一條基本原則。因此,我們可以基于異步通信改寫上面的程序。 JavaScript要求以一種事件驅(qū)動的程序設(shè)計方式來寫異步通信程序。在很多場合中,你都必須指定一個回調(diào)程序,一旦收到通信響應(yīng),這個函數(shù)就會被調(diào)用。例如,上面定義的getArticleWithCache()可以寫成這樣:
復(fù)制代碼 代碼如下:
var cache = {};
function getArticleWithCache ( id, callback ) {
if ( !cache[id] ) {
callback(cache[id]);
} else {
getArticle(id, function( a ){
cache[id] = a;
callback(a);
});
}
}

這個程序也在內(nèi)部調(diào)用了getArticle()函數(shù)。然而需要注意的是,為異步通信設(shè)計的這版getArticle()函數(shù)要接收一個函數(shù)作為第二個參數(shù)。當(dāng)調(diào)用這個getArticle()函數(shù)時,與從前一樣要給服務(wù)器發(fā)送一個請求,不同的是,現(xiàn)在函數(shù)會迅速返回而非等待服務(wù)器的響應(yīng)。這意味著,當(dāng)執(zhí)行權(quán)交回給調(diào)用程序時,還沒有得到服務(wù)器的響應(yīng)。如此一來,線程就可以去執(zhí)行其它任務(wù)直至獲得服務(wù)器響應(yīng),并在此時調(diào)用回調(diào)函數(shù)。一旦得到服務(wù)器響應(yīng), getArticle()的第二個參數(shù)作為預(yù)先定義的回調(diào)函數(shù)就要被調(diào)用,服務(wù)器的返回值即為其參數(shù)。同樣的,getArticleWithCache ()也要做些改變,定義一個回調(diào)參數(shù)作為其第二個參數(shù)。這個回調(diào)函數(shù)將在被傳給getArticle()的回調(diào)函數(shù)中調(diào)用,因而它可以在服務(wù)器通信結(jié)束后被執(zhí)行。

單是上面這些改動你可能已經(jīng)認為相當(dāng)復(fù)雜了,但是對backgroundLoad()函數(shù)做得改動將會更復(fù)雜,它也要被改寫成可以處理回調(diào)函數(shù)的形式:
復(fù)制代碼 代碼如下:
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(),再應(yīng)用已經(jīng)獲得子文章ID遞歸調(diào)用backgroundLoad ()。不過同樣是對數(shù)組的循環(huán)訪問,新函數(shù)中的就不太好辨認了,以前的程序中是用一個for循環(huán)語句完成的。為什么實現(xiàn)同樣功能的兩套函數(shù)是如此的大相徑庭呢?

這個差異源于一個事實:任何函數(shù)在遇到有需要同服務(wù)器進行通信情況后,都必須立刻返回,例如getArticleWithCache()。除非原來的函數(shù)不在執(zhí)行當(dāng)中,否則應(yīng)當(dāng)接受服務(wù)器響應(yīng)的回調(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ù)傳送風(fēng)格(CPS)的人來說,這就是一個 CPS的手動實現(xiàn),因為不能使用循環(huán)語法,所以即便如前面提到的遍歷樹那么簡單的程序也得寫得很復(fù)雜。與事件驅(qū)動程序設(shè)計相關(guān)的問題是控制流問題:循環(huán)和其它控制流表達式可能比較難理解。

這里還有另外一個問題:如果你把一個沒有應(yīng)用異步通信的函數(shù)轉(zhuǎn)換為一個使用異步通信的函數(shù),那么重寫的函數(shù)將需要一個回調(diào)函數(shù)作為新增參數(shù),這為已經(jīng)存在的APIs造成了很大問題,因為內(nèi)在的改變沒有把影響限于內(nèi)部,而是導(dǎo)致整體混亂的APIs以及API的其它使用者的改變。

造成這些問題目的根本原因是什么呢?沒錯,正是JavaScript單線程機制導(dǎo)致了這些問題。在單線程里執(zhí)行異步通信需要事件驅(qū)動程序設(shè)計和復(fù)雜的語句。如果當(dāng)程序在等待服務(wù)器的響應(yīng)時,有另外一個線程可以來處理用戶請求,那么上述復(fù)雜技術(shù)就不需要了。

試試多線程編程
讓我來介紹一下Concurrent.Thread,它是一個允許JavaScript進行多線程編程的庫,應(yīng)用它可以大大緩解上文提及的在AJAX開發(fā)中與異步通信相關(guān)的困難。這是一個用JavaScript寫成的免費的軟件庫,使用它的前提是遵守Mozilla Public License和GNU General Public License這兩個協(xié)議。你可以從他們的網(wǎng)站 下載源代碼。

馬上來下載和使用源碼吧!假定你已經(jīng)將下載的源碼保存到一個名為Concurrent.Thread.js的文件夾里,在進行任何操作之前,先運行如下程序,這是一個很簡單的功能實現(xiàn):
復(fù)制代碼 代碼如下:
<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),你可以滾屏來看它。現(xiàn)在讓我們來仔細研究一下代碼,他應(yīng)用while(1)條件制造了一個不會中止的循環(huán),通常情況下,象這樣不斷使用一個并且是唯一一個線程的JavaScript程序會導(dǎo)致瀏覽器看起來象凍結(jié)了一樣,自然也就不會允許你滾屏。那么為什么上面的這段程序允許你這么做呢?關(guān)鍵之處在于while(1)上面的那條Concurrent.Thread.create()語句,這是這個庫提供的一個方法,它可以創(chuàng)建一個新線程。被當(dāng)做參數(shù)傳入的函數(shù)在這個新線程里執(zhí)行,讓我們對程序做如下微調(diào):
復(fù)制代碼 代碼如下:
<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()可以重復(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)這個目的。例如,前面那個例子可以這樣寫:
復(fù)制代碼 代碼如下:
<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)。你應(yīng)該注意到標簽內(nèi)的type屬性,那里是一個很陌生的值(text/x- script.multithreaded-js),如果這個屬性被放在script標簽內(nèi),那么Concurrent.Thread就會在一個新的線程內(nèi)執(zhí)行標簽之間的程序。你應(yīng)當(dāng)記住一點,在本例一樣,必須將Concurrent.Thread庫包含進來。

有了Concurrent.Thread,就有可能自如的將執(zhí)行環(huán)境在線程之間進行切換,即使你的程序很長、連續(xù)性很強。我們可以簡要地討論下如何執(zhí)行這種操作。簡言之,需要進行代碼轉(zhuǎn)換。粗略地講,首先要把傳遞給create()的函數(shù)轉(zhuǎn)換成一個字符串,接著改寫直至它可以被分批分次執(zhí)行。然后這些程序可以依照調(diào)度程序逐步執(zhí)行。調(diào)度程序負責(zé)協(xié)調(diào)多線程,換句話說,它可以在適當(dāng)?shù)臅r候做出調(diào)整以便每一個修改后的函數(shù)都會得到同等機會運行。 Concurrent.Thread實際上并沒有創(chuàng)建新的線程,僅僅是在原本單線程的基礎(chǔ)上模擬了一個多線程環(huán)境。

雖然轉(zhuǎn)換后的函數(shù)看起來是運行在不同的線程內(nèi),但是實際上只有一個線程在做這所有的事情。在轉(zhuǎn)換后的函數(shù)內(nèi)執(zhí)行同步通信仍然會造成瀏覽器凍結(jié),你也許會認為以前的那些問題根本就沒有解決。不過你不必耽心,Concurrent.Thread提供了一個應(yīng)用JavaScript 的異步通信方式實現(xiàn)的定制通信庫,它被設(shè)計成當(dāng)一個線程在等待服務(wù)器的響應(yīng)時允許其它線程運行。這個通信庫存于 Concurrent.Thread.Http下。它的用法如下所示:
復(fù)制代碼 代碼如下:
<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()方法與服務(wù)器交互,當(dāng)?shù)玫?a href=/yuedu/fuwuqi/ target=_blank class=infotextkey>服務(wù)器的響應(yīng)后就返回一個XMLHttpRequest對象作為返回值。當(dāng)get()方法返回時,已經(jīng)收到了服務(wù)器響應(yīng),所以就沒必要再用回調(diào)函數(shù)接收結(jié)果。自然,也不必再耽心當(dāng)程序等待服務(wù)器的響應(yīng)時瀏覽器凍結(jié)的情況了。另外,還有一個 post()方法可以用來發(fā)送數(shù)據(jù)到服務(wù)器
復(fù)制代碼 代碼如下:
<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)了第一個例子當(dāng)中的getArticle()方法,那么你很快就能應(yīng)用文章開頭示例的那種簡單的方法寫出getArticleWithCache(),backgroundLoad ()以及其它調(diào)用了getArticle()方法的函數(shù)了。即使是那版backgroundLoad()正在讀文章數(shù)據(jù),照例還有另外一個線程可以對用戶請求做出響應(yīng),瀏覽器因此也不會凍結(jié)。現(xiàn)在,你能理解在JavaScript中應(yīng)用多線程有多實用了?

想了解更多
我向你介紹了一個可以在JavaScript中應(yīng)用多線程的庫:Concurrent.Thread。這篇文章的內(nèi)容只是很初級的東西,如果你想更深入的了解,我推薦您去看the tutorial。它提供有關(guān)Concurrent.Thread用法的更多內(nèi)容,并列出了可供高級用戶使用的文檔,是最適合起步的材料。訪問他們的網(wǎng)站也不錯,那里提供更多信息。

有關(guān)作者
Daisuke Maki:從International Christian大學(xué)文科學(xué)院自然科學(xué)分部畢業(yè)后(取得文學(xué)學(xué)士學(xué)位),又在Electro-Communications大學(xué)的研究生院信息專業(yè)攻讀碩士學(xué)位。擅長Web開發(fā)和應(yīng)用JavaScript的AJAX。他開發(fā)了Concurrent.Thread。2006財政年度在日本信息技術(shù)促進機構(gòu)(IPA)指導(dǎo)的項目Explatory Software Project中應(yīng)用了這個設(shè)計。

目前已經(jīng)擁有一個工學(xué)碩士學(xué)位的他正在Electro-Communications大學(xué)的研究生院注冊攻讀博士學(xué)位。

JavaScript技術(shù)JavaScript多線程的實現(xiàn)方法,轉(zhuǎn)載需保留來源!

鄭重聲明:本文版權(quán)歸原作者所有,轉(zhuǎn)載文章僅為傳播更多信息之目的,如作者信息標記有誤,請第一時間聯(lián)系我們修改或刪除,多謝。

主站蜘蛛池模板: a级男女性高爱潮高清试看 A级毛片无码久久精品免费 | 久久91精品久久久久久水蜜桃 | 迅雷哥在线观看高清 | 一本二卡三卡四卡乱码麻豆 | JIZZ19学生第一次 | 艳鉧动漫片1~6全集在线 | 亚洲中文无码AV在线观看 | 三级网站午夜三级 | 亚洲国产精品线在线观看 | 男人的天堂久久精品激情a 男人的天堂黄色片 | 国产精品自产拍在线观看网站 | 99热在线观看 | 91福利国产在线观看网站 | 麻豆精品乱码WWW久久密 | 偷拍亚洲色自拍 | 国产av免费观看日本 | 久久精品国产午夜伦班片 | 国产精品人妻系列21P | 久久性综合亚洲精品电影网 | 久久久久久久久免费视频 | 国产中文在线 | 国产AV一区二区三区日韩 | 国产一区二区三区在线看片 | 电影日本妻子 | 出差无套内射小秘书 | 久久国产欧美日韩精品免费 | 好男人好资源在线观看 | 久久er国产精品免费观看2 | 精品国产乱码久久久久久夜深人妻 | lesbabes性欧美 | 在线视频一区二区三区在线播放 | 91久久线看在观草草青青 | 日韩经典欧美一区二区三区 | 国产亚洲精品久久久久久无码网站 | 国产精品久久精品 | 色欲av蜜臀av高清 | 免费高清毛片 | 秀婷程仪公欲息肉婷在线观看 | 亚洲高清国产品国语在线观看 | 亚洲精品国偷拍自产在线观看蜜臀 | 老湿司午夜爽爽影院榴莲视频 |