更多扩展
.NET基础类库针对IEnumerable定义了大量的函数式的辅助方法,开发人员可以直接将它们组合运用在项目中。除了标准的LINQ操作方法之外,响应式框架中同样定义了大量辅助方法,可以配合LINQ to Observable组合使用。例如本文开头所设想的鼠标“拖动及绘图”功能,便可以使用如下代码完成:
var mouseMove = GetMouseMove(); var mouseDiff = mouseMove.Zip(mouseMove.Skip(1), (prev, curr) => new { PrevPos = new Point(prev.EventArgs.X, prev.EventArgs.Y), CurrPos = new Point(curr.EventArgs.X, curr.EventArgs.Y) }); var mouseDrag = from _ in GetMouseDown() from diff in mouseDiff.TakeUntil(GetMouseUp()) select diff; mouseDrag.Subscribe(diff => DrawLine(diff.PrevPos, diff.CurrPos));
在这段代码中,我们首先将mouseMove事件使用Skip跳开一项,再与自身通过Zip方法组合成mouseDiff,这是一个输出相邻两次MouseMove事件坐标的数据源;接着,我们利用LINQ从触发MouseDown事件开始,向mouseDiff数据源获取每一项diff,直至(TakeUntil)触发MouseUp事件,以此生成最终的mouseDrag;最后再将绘图功能订阅至这个数据源上。您会发现此时我们已经无须手动维护操作过程中的各种状态了,从事件的“开始”到“结束”均使用响应式框架的辅助方法“声明”而来。
以上便是一个利用了Skip,Zip,TakeUntil等辅助方法的例子。当然,这些辅助方法在IEnumerable上都有语义相同的对应操作,而在响应式框架中还有更多辅助方法是针对特性异步场景的。假设我们现在要编写一个即时翻译功能,同时发起三个请求,将中文分别翻译至英语、法语及西班牙语,并显示最先返回的两个结果(真是个奇怪的需求)。此外,我们不会在用户输入每个字符的时候便发起一个远程请求,而是在用户停止输入0.5秒之后才根据当前的输入框中的文字进行提示。于是我们可以编写这样的代码:
var limit = TimeSpan.FromSeconds(0.5); var translate = from _ in GetKeyPress().Throttle(limit) let text = this.txtInput.Text where text.Length > 0 let english = Bing.Translate(text, "en") let french = Bing.Translate(text, "fr") let spanish = Bing.Translate(text, "es") from result in Observable.Join( english.And(french).Then((en, fr) => new { English = en, French = fr, Spanish = "" }), english.And(spanish).Then((en, es) => new { English = en, French = "", Spanish = es }), french.And(spanish).Then((fr, es) => new { English = "", French = fr, Spanish = es })) select result;translate.Subscribe(...);
这里用到了Throottle方法,它会过滤某个数据源的输出,确保在该数据源“静默”特定时间之后,才将最近的一条数据推送至外部。此外,这里还使用了Observable.Join方法控制多个数据源,根据返回结果的先后获得合适的结果。响应式框架提供了大量针对某种异步场景的辅助方法,例如用于定期推送数据的Interval方法,从一个数据源根据特定条件进行采样的Sample方法,合并多个数据源的ForkJoin方法,以及表示流程控制的For,While,If等等。这些方法内部会维护各种所需要的状态,为我们打理各种复杂的竞争情况,以此节省了开发人员的精力。
如果这些还不能满足我们的要求,我们也可以根据自己的需要开发特定的辅助方法,就像我们在使用LINQ to Object时为IEnumerable所作的各种扩展那样。响应式框架也提供了一系列Subject类型,简化了IObservable自定义扩展的开发过程。由于响应式框架尚未正式发布,微软目前建立了一个Wiki,用于展示关于各辅助方法及Subject类的使用示例及其他相关信息。
响应式框架的JavaScript版本
响应式编程的重要使用场景之一便是与用户交互的GUI界面。例如,Silverlight禁止任何阻塞的IO操作,换言之Silverlight中的所有网络操作都是异步的,微软也正是出于简化异步开发的目的才设计了响应式框架(事实上响应式框架已经集成到Silverlight Toolkit中)。不过与Silverlight相比,基于浏览器的原生JavaScript应用程序无疑使用地更为广泛。对于这样的应用程序来说,动画是异步的,AJAX请求也是异步的,我们几乎可以断言,如果有一套面向JavaScirpt应用程序的响应式框架,一定会比面向Silverlight的框架更有意义得多。
微软也想到了这一点。之前我们讨论的“响应式框架”,其实只是响应式编程模型的一种实现。更确切地说,我们只是讨论了这套框架的.NET版本,微软还提供了JavaScript版本的响应式框架。JavaScript版本的API与.NET版本几乎完全一致,例如我们之前讨论的拖放操作,使用JavaScript即可写作:
var target = $("#dragTarget"); var mouseMove = target.toObservable("mousemove"); var mouseDiff = mouseMove.Zip(mouseMove.Skip(1), function(prev, curr) { return { PrevPos: { x: prev.clientX, y: prev.clientY }, CurrPos: { x: curr.clientX, y: curr.clientY } }; }); var mouseDown = target.toObservable("mousedown"); var mouseUp = target.toObservable("mouseup"); var mouseDrag = mouseDown.SelectMany(function() { mouseDiff.TakeUntil(mouseUp); });mouseDrag.Subscribe(...);
由于没有C#中的LINQ查询语言,我们只能直接使用展开后的方法,如SelectMany来编写逻辑。JavaScript版本的响应式框架还提供了一系列的“胶合”层,能够与jQuery,Dojo,MooTools,Prototype等流行框架同时使用。例如,上一段代码中的toObservable便是在jQuery根对象上扩展的方法。
总结
异步编程在用户交互式界面及一些云计算场景中尤其重要。微软的云编程能力团队针对.NET平台和JavaScirpt分别提供了一套响应式框架,希望以此简化异步程序的开发。不过,这套响应式框架所表现出的理念是通用的。而且,事实上只要是拥有匿名函数及闭包的语言,例如Scala,Python,Ruby等等,实现这样一套框架其实都不是十分困难的事情。
原文:http://blog.zhaojie.me/2010/09/async-programming-and-reactive-framework.html
本文链接:http://www.blueidea.com/tech/program/2010/7999.asp
出处:老赵点滴
责任编辑:bluehearts
上一页 异步编程与响应式框架 [3] 下一页
◎进入论坛网络编程版块参加讨论
|