翻译了一篇kuwamoto以前的一篇关于如何处理异步事件的日志。这一系列共有三部分,我会陆续把他们都翻译出来,今天先贴出来第一部分。
原文地址:http://kuwamoto.org/2006/05/16/dealing-with-asynchronous-events-part-1/
原文作者:
Sho Kuwamoto
译者:
Dreamer (http://www.zhuoqun.net)
_______________________________________________________________________________
处理异步事件,第一部分
使用异步事件模型的一个困难就是很难写出易读的代码。
<}0{>
这对于
AJAX
以及
Flex
应用程序都是同样真实的道理。
过去几个月以来,我针对这个问题尝试了各种各样的方法。我认为这能帮助我漫步在曾经尝试的各种代码中,并以此说明不同的方法。
让我们以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!
}
问题是结果并不是立刻就返回。代码在执行而结果可能在数百毫秒内无法返回。此外,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);
}
这并不太差劲,是吗?现在,想象一下现在我需要在结果函数中使用albumId,而恰好RPC send()方法就有一个叫做呼叫对象(call object)的特殊对象来让我们那样做。
使用呼叫对象来传递参数
呼叫对象是一个一旦结果事件(result event)触发就会被传送到结果处理程序的对象。
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);
}
现在,假设我需要把这些呼叫串在一起。它会变得非常凌乱不堪。
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之前绑定第一个函数,在第二个send之前绑定第二个函数:
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();
}
如果你对异步编程很熟悉的话,你马上就会明白问题在哪里。对于那些不熟悉的人,这里列出了将会发生的事件的顺序:
<!--[if !supportLists]-->1.<!--[endif]-->一个“result”的监听器(listener)将会绑定到getAlbumInfoResult()。
<!--[if !supportLists]-->2.<!--[endif]-->建立album info请求。
<!--[if !supportLists]-->3.<!--[endif]-->先前的监听器被移除。
<!--[if !supportLists]-->4.<!--[endif]-->“result”的一个新的监听器被绑定到getAlbumArtResult()。
<!--[if !supportLists]-->5.<!--[endif]-->建立album art 请求。
<!--[if !supportLists]-->6.<!--[endif]-->等待一段时间。
<!--[if !supportLists]-->7.<!--[endif]-->由于网络没有任何规律,album info的结果返回或者是album art的结果返回。
<!--[if !supportLists]-->8.<!--[endif]-->不管哪个呼叫返回,它都会找到getAlbumArtResult()。因为这个时候它是唯一注册的监听器。
解决多个呼叫(mutiple call)的问题
解决多个呼叫问题的传统方法就是吧callback函数附加到呼叫对象上。这样可以正常工作,因为每次service被调用的时候一个唯一的呼叫对象就会被创建。
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的调用会呼叫同一个处理函数,但是实际上被呼叫的处理函数是隐藏在呼叫对象中的那个。在第一次呼叫的情况中,getAlbumInfoResult()将会被呼叫,而在第二种情况下,getAlbumArtResult()将会被呼叫。
posted on 2007-02-28 19:51
☜♥☞MengChuChen 阅读(769)
评论(0) 编辑 收藏 所属分类:
flex2.0