原文地址:
http://kuwamoto.org/2006/05/16/dealing-with-asynchronous-events-part-1/原文作者:ShoKuwamoto
译者:Dreamer
使用异步事件模型的一个困难就是很难写出易读的代码。这对于 AJAX 以及 Flex 应用程序都是同样真实的道理。
过去几个月以来,我针对这个问题尝试了各种各样的方法。我认为这能帮助我漫步在曾经尝试的各种代码中,并以此说明不同的方法。
让我们以RPC service 调用为例。假设我正在使用一个http service来获得一个唱片的信息:
public function getAlbumInfo(albumId: int) : void
- {
- myService.request = { type: "album", albumId: albumId };
- myService.send();
-
-
- }
问题是结果并不是立刻就返回。代码在执行而结果可能在数百毫秒内无法返回。
此外,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
- {
-
-
- 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 };
-
- var call : Object = myService.send();
- call.albumId = albumId;
- }
- public function getAlbumInfoResult(event: Event) : void
- {
-
-
- myAlbumList.addAlbum(event.call.albumId, event.result.album);
- }
现在,假设我需要把这些呼叫串在一起。它会变得非常凌乱不堪。
public function getAlbumInfo(albumId: int) : void
- {
- myService.addEventListener("result", getAlbumInfoResult);
- myService.request = { type: "album", albumId: albumId };
-
- 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 };
-
- var call = myService.send();
- call.artistId = artistId;
- }
- public function getArtistInfoResult(event: Event) : void
- {
- myArtistList.addArtist(event.call.artistId, event.result.artist);
- }
下面,让我们假设一种更复杂的情形:如果多个呼叫程序需要调用同一个HTTPservice呢?你如何确保正确地处理返回结果?
多个呼叫的问题 让我们把上面的代码做点简单的改变来阐明这个问题。
public function getAlbumInfo(albumId: int) : void
- {
-
- myService.addEventListener("result", getAlbumInfoResult);
-
- myService.request = { type: "album", albumId: albumId };
- myService.send();
-
- myService.request = { type: "albumArt", albumId: albumId };
- myService.send();
- }
- public function getAlbumInfoResult(event: Event) : void
- {
-
-
- }
对这个问题的愚蠢的解决方法就是在第一个send之前绑定第一个函数,在第二个send之前绑定第二个函数:
public function getAlbumInfo(albumId: int) : void
- {
-
- myService.addEventListener("result", getAlbumInfoResult);
-
- myService.request = { type: "album", albumId: albumId };
- myService.send();
-
- myService.removeEventListener("result", getAlbumInfoResult);
- myService.addEventListener("result", getAlbumArtResult);
-
- myService.request = { type: "albumArt", albumId: albumId };
- myService.send();
- }
如果你对异步编程很熟悉的话,你马上就会明白问题在哪里。对于那些不熟悉的人,这里列出了将会发生的事件的顺序:
1. 一个“result”的监听器(listener)将会绑定到 getAlbumInfoResult()。
2. 建立 album info请求。
3. 先前的监听器被移除。
4. “result”的一个新的监听器被绑定到 getAlbumArtResult()。
5. 建立 album art 请求。
6. 等待一段时间。
7. 由于网络没有任何规律,album info的结果返回或者是 album art 的结果返回。
8. 不管哪个呼叫返回,它都会找到 getAlbumArtResult()。因为这个时候它是唯一注册的监听器。
解决多个呼叫(mutiple call)的问题解决多个呼叫问题的传统方法就是吧callback函数附加到呼叫对象上。这样可以正常工作,因为每次service被调用的时候一个唯一的呼叫对象就会被创建。
public function doInit()
- {
- myService.addEventListener("result", handleResult);
- }
- public function getAlbumInfo(albumId: int) : void
- {
- var call : Object;
-
- myService.request = { type: "album", albumId: albumId };
- call = myService.send();
- call.handler = getAlbumInfoResult;
-
- myService.request = { type: "albumArt", albumId: albumId };
- call = myService.send();
- call.handler = getAlbumArtResult;
- }
- public function handleResult(event: ResultEvent) : void
- {
-
- var call : Object = event.call;
-
- var handler : Function = call.handler;
-
-
- handler.call(null, event);
- }
这在个新的代码中,两个对service的调用会呼叫同一个处理函数,但是实际上被呼叫的处理函数是隐藏在呼叫对象中的那个。在第一次呼叫的情况中,getAlbumInfoResult()将会被呼叫,而在第二种情况下,getAlbumArtResult()将会被呼叫。
待续...