翻譯了一篇kuwamoto以前的一篇關于如何處理異步事件的日志。這一系列共有三部分,我會陸續(xù)把他們都翻譯出來,今天先貼出來第一部分。
原文地址:http://kuwamoto.org/2006/05/16/dealing-with-asynchronous-events-part-1/
原文作者:
Sho Kuwamoto
譯者:
Dreamer (http://www.zhuoqun.net)
_______________________________________________________________________________
處理異步事件,第一部分
使用異步事件模型的一個困難就是很難寫出易讀的代碼。
<}0{>
這對于
AJAX
以及
Flex
應用程序都是同樣真實的道理。
過去幾個月以來,我針對這個問題嘗試了各種各樣的方法。我認為這能幫助我漫步在曾經(jīng)嘗試的各種代碼中,并以此說明不同的方法。
讓我們以RPC service 調用為例。假設我正在使用一個http service來獲得一個唱片的信息:
public function getAlbumInfo(albumId: int) : void
{
myService.request = { type: "album", albumId: albumId };
myService.send();
// I'd like to do something with the results of
// my request, but I can't!
}
問題是結果并不是立刻就返回。代碼在執(zhí)行而結果可能在數(shù)百毫秒內無法返回。此外,Flash Player不提供一個阻滯的方法來等待結果就緒。為了相應最終返回的結果,你需要俘獲一個事件。
public function getAlbumInfo(albumId: int) : void
{
myService.addEventListener("result", getAlbumInfoResult);
myService.request = { type: "album", albumId: albumId };
myService.send();
}
public function getAlbumInfoResult(event: Event) : void
{
// Hundreds of milliseconds later, my results
// have arrived, and I can add them to my list!
myAlbumList.addAlbum(event.result.album);
}
這并不太差勁,是嗎?現(xiàn)在,想象一下現(xiàn)在我需要在結果函數(shù)中使用albumId,而恰好RPC send()方法就有一個叫做呼叫對象(call object)的特殊對象來讓我們那樣做。
使用呼叫對象來傳遞參數(shù)
呼叫對象是一個一旦結果事件(result event)觸發(fā)就會被傳送到結果處理程序的對象。
public function getAlbumInfo(albumId: int) : void
{
myService.addEventListener("result", getAlbumInfoResult);
myService.request = { type: "album", albumId: albumId };
// Make the call, and save a value for later use.
var call : Object = myService.send();
call.albumId = albumId;
}
public function getAlbumInfoResult(event: Event) : void
{
// Use the albumId value that was passed to me from above
// as part of the call object.
myAlbumList.addAlbum(event.call.albumId, event.result.album);
}
現(xiàn)在,假設我需要把這些呼叫串在一起。它會變得非常凌亂不堪。
public function getAlbumInfo(albumId: int) : void
{
myService.addEventListener("result", getAlbumInfoResult);
myService.request = { type: "album", albumId: albumId };
// Save the albumId for use later!
var call : Object = myService.send();
call.albumId = albumId;
}
public function getAlbumInfoResult(event: Event) : void
{
var artistId: int = event.result.album.artistId;
myAlbumList.addAlbum(event.call.albumId, event.result.album);
myService.addEventListener("result", getArtistInfoResult);
myService.request = { type: "artist", artistId : artistId };
// Now, save the artistId for use later!
var call = myService.send();
call.artistId = artistId;
}
public function getArtistInfoResult(event: Event) : void
{
myArtistList.addArtist(event.call.artistId, event.result.artist);
}
下面,讓我們假設一種更復雜的情形:如果多個呼叫程序需要調用同一個HTTP service呢?你如何確保正確地處理返回結果?
多個呼叫的問題
讓我們把上面的代碼做點簡單的改變來闡明這個問題。
public function getAlbumInfo(albumId: int) : void
{
// Wire up my result handler.
myService.addEventListener("result", getAlbumInfoResult);
// Ask for the album info.
myService.request = { type: "album", albumId: albumId };
myService.send();
// Also ask for the album art.
myService.request = { type: "albumArt", albumId: albumId };
myService.send();
}
public function getAlbumInfoResult(event: Event) : void
{
// At this point, I have no idea whether I should be
// handling the album info or the album art.
}
對這個問題的愚蠢的解決方法就是在第一個send之前綁定第一個函數(shù),在第二個send之前綁定第二個函數(shù):
public function getAlbumInfo(albumId: int) : void
{
// Wire up my result handler.
myService.addEventListener("result", getAlbumInfoResult);
// Ask for the album info.
myService.request = { type: "album", albumId: albumId };
myService.send();
// BUG!! This will not work!
myService.removeEventListener("result", getAlbumInfoResult);
myService.addEventListener("result", getAlbumArtResult);
// Also ask for the album art.
myService.request = { type: "albumArt", albumId: albumId };
myService.send();
}
如果你對異步編程很熟悉的話,你馬上就會明白問題在哪里。對于那些不熟悉的人,這里列出了將會發(fā)生的事件的順序:
<!--[if !supportLists]-->1.<!--[endif]-->一個“result”的監(jiān)聽器(listener)將會綁定到getAlbumInfoResult()。
<!--[if !supportLists]-->2.<!--[endif]-->建立album info請求。
<!--[if !supportLists]-->3.<!--[endif]-->先前的監(jiān)聽器被移除。
<!--[if !supportLists]-->4.<!--[endif]-->“result”的一個新的監(jiān)聽器被綁定到getAlbumArtResult()。
<!--[if !supportLists]-->5.<!--[endif]-->建立album art 請求。
<!--[if !supportLists]-->6.<!--[endif]-->等待一段時間。
<!--[if !supportLists]-->7.<!--[endif]-->由于網(wǎng)絡沒有任何規(guī)律,album info的結果返回或者是album art的結果返回。
<!--[if !supportLists]-->8.<!--[endif]-->不管哪個呼叫返回,它都會找到getAlbumArtResult()。因為這個時候它是唯一注冊的監(jiān)聽器。
解決多個呼叫(mutiple call)的問題
解決多個呼叫問題的傳統(tǒng)方法就是吧callback函數(shù)附加到呼叫對象上。這樣可以正常工作,因為每次service被調用的時候一個唯一的呼叫對象就會被創(chuàng)建。
public function doInit()
{
myService.addEventListener("result", handleResult);
}
public function getAlbumInfo(albumId: int) : void
{
var call : Object;
// Ask for the album info.
myService.request = { type: "album", albumId: albumId };
call = myService.send();
call.handler = getAlbumInfoResult;
// Also ask for the album art.
myService.request = { type: "albumArt", albumId: albumId };
call = myService.send();
call.handler = getAlbumArtResult;
}
public function handleResult(event: ResultEvent) : void
{
// Retrieve the call object from the event.
var call : Object = event.call;
// Get the handler.
var handler : Function = call.handler;
// Call the handler. We use handler.call(xxx) instead
// of handler(xxx) to properly deal with the scope chain.
handler.call(null, event);
}
這在個新的代碼中,兩個對service的調用會呼叫同一個處理函數(shù),但是實際上被呼叫的處理函數(shù)是隱藏在呼叫對象中的那個。在第一次呼叫的情況中,getAlbumInfoResult()將會被呼叫,而在第二種情況下,getAlbumArtResult()將會被呼叫。
posted on 2007-02-28 19:51
???MengChuChen 閱讀(778)
評論(0) 編輯 收藏 所屬分類:
flex2.0