【永恒的瞬间】
☜Give me hapy ☞

翻译了一篇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

只有注册用户登录后才能发表评论。


网站导航: