更多擴展
.NET基礎類庫針對IEnumerable定義了大量的函數(shù)式的輔助方法,開發(fā)人員可以直接將它們組合運用在項目中。除了標準的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事件坐標的數(shù)據(jù)源;接著,我們利用LINQ從觸發(fā)MouseDown事件開始,向mouseDiff數(shù)據(jù)源獲取每一項diff,直至(TakeUntil)觸發(fā)MouseUp事件,以此生成最終的mouseDrag;最后再將繪圖功能訂閱至這個數(shù)據(jù)源上。您會發(fā)現(xiàn)此時我們已經(jīng)無須手動維護操作過程中的各種狀態(tài)了,從事件的“開始”到“結(jié)束”均使用響應式框架的輔助方法“聲明”而來。
以上便是一個利用了Skip,Zip,TakeUntil等輔助方法的例子。當然,這些輔助方法在IEnumerable上都有語義相同的對應操作,而在響應式框架中還有更多輔助方法是針對特性異步場景的。假設我們現(xiàn)在要編寫一個即時翻譯功能,同時發(fā)起三個請求,將中文分別翻譯至英語、法語及西班牙語,并顯示最先返回的兩個結(jié)果(真是個奇怪的需求)。此外,我們不會在用戶輸入每個字符的時候便發(fā)起一個遠程請求,而是在用戶停止輸入0.5秒之后才根據(jù)當前的輸入框中的文字進行提示。于是我們可以編寫這樣的代碼:
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方法,它會過濾某個數(shù)據(jù)源的輸出,確保在該數(shù)據(jù)源“靜默”特定時間之后,才將最近的一條數(shù)據(jù)推送至外部。此外,這里還使用了Observable.Join方法控制多個數(shù)據(jù)源,根據(jù)返回結(jié)果的先后獲得合適的結(jié)果。響應式框架提供了大量針對某種異步場景的輔助方法,例如用于定期推送數(shù)據(jù)的Interval方法,從一個數(shù)據(jù)源根據(jù)特定條件進行采樣的Sample方法,合并多個數(shù)據(jù)源的ForkJoin方法,以及表示流程控制的For,While,If等等。這些方法內(nèi)部會維護各種所需要的狀態(tài),為我們打理各種復雜的競爭情況,以此節(jié)省了開發(fā)人員的精力。
如果這些還不能滿足我們的要求,我們也可以根據(jù)自己的需要開發(fā)特定的輔助方法,就像我們在使用LINQ to Object時為IEnumerable所作的各種擴展那樣。響應式框架也提供了一系列Subject類型,簡化了IObservable自定義擴展的開發(fā)過程。由于響應式框架尚未正式發(fā)布,微軟目前建立了一個Wiki,用于展示關于各輔助方法及Subject類的使用示例及其他相關信息。
響應式框架的JavaScript版本
響應式編程的重要使用場景之一便是與用戶交互的GUI界面。例如,Silverlight禁止任何阻塞的IO操作,換言之Silverlight中的所有網(wǎng)絡操作都是異步的,微軟也正是出于簡化異步開發(fā)的目的才設計了響應式框架(事實上響應式框架已經(jīng)集成到Silverlight Toolkit中)。不過與Silverlight相比,基于瀏覽器的原生JavaScript應用程序無疑使用地更為廣泛。對于這樣的應用程序來說,動畫是異步的,AJAX請求也是異步的,我們幾乎可以斷言,如果有一套面向JavaScirpt應用程序的響應式框架,一定會比面向Silverlight的框架更有意義得多。
微軟也想到了這一點。之前我們討論的“響應式框架”,其實只是響應式編程模型的一種實現(xiàn)。更確切地說,我們只是討論了這套框架的.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根對象上擴展的方法。
總結(jié)
異步編程在用戶交互式界面及一些云計算場景中尤其重要。微軟的云編程能力團隊針對.NET平臺和JavaScirpt分別提供了一套響應式框架,希望以此簡化異步程序的開發(fā)。不過,這套響應式框架所表現(xiàn)出的理念是通用的。而且,事實上只要是擁有匿名函數(shù)及閉包的語言,例如Scala,Python,Ruby等等,實現(xiàn)這樣一套框架其實都不是十分困難的事情。
原文:http://blog.zhaojie.me/2010/09/async-programming-and-reactive-framework.html
本文鏈接:http://www.95time.cn/tech/program/2010/7999.asp
出處:老趙點滴
責任編輯:bluehearts
上一頁 異步編程與響應式框架 [3] 下一頁
◎進入論壇網(wǎng)絡編程版塊參加討論
|