primitivejqueryeach

jqueryeach  时间:2021-05-17  阅读:()
DEVHOL203–CuringtheasynchronousblueswiththeReactiveExtensionsforJavaScriptIntroductionThisHands-on-Lab(HOL)familiarizesthereaderwiththeReactiveExtensionsforJavaScript(RxJS).
Byexploringtheframeworkthroughaseriesofincrementalsamples,thereadergetsafeelforRx'scompositionalpowerusedtowriteasynchronousapplications,basedontheconceptofobservablecollections.
PrerequisitesWeassumethefollowingintellectualandmaterialprerequisitesinordertocompletethislabsuccessfully:ActiveknowledgeoftheJavaScriptprogramminglanguageandfamiliaritywiththejQuerylibrary.
Feelingfortheconceptofasynchronousprogrammingandrelatedcomplexities.
VisualStudio2010and.
NET4(priorversionscanbeusedbutthelabisbuiltforVS2010)installation.
InstallationofRxforJavaScriptfromMSDNDevLabsathttp://msdn.
microsoft.
com/en-us/devlabs.
WhatisRxRxcanbesummarizedinthefollowingsentencewhichcanalsobereadontheDevLabshomepage:Rxisalibraryforcomposingasynchronousandevent-basedprogramsusingobservablecollections.
Threecorepropertiesarereflectedinhere,allofwhichwillbeaddressedthroughoutthislab:Asynchronousandevent-based–Asreflectedinthetitle,thebreadandbutterofRx'smissionstatementistosimplifythoseprogrammingmodels.
Everyoneknowswhatstuckuserinterfaceslooklike,bothontheWindowsplatformandontheweb.
Andwiththecloudaroundthecorner,asynchronybecomesquintessential.
Low-leveltechnologieslikeDOMandcustomevents,asynchronouspatterns,AJAX,etc.
areoftentoohard.
Composition–Combiningasynchronouscomputationstodayiswaytoohard.
Itinvolvesalotofplumbingcodethathaslittletonothingtodowiththeproblembeingsolved.
Inparticular,thedataflowoftheoperationsinvolvedintheproblemisnotclearatall,andcodegetsspreadoutthroughouteventhandlers,asynchronouscallbackprocedures,andwhatnot.
Observablecollections–Bylookingatasynchronouscomputationsasdatasources,wecanleveragetheactiveknowledgeofLINQ'sprogrammingmodel.
That'sright:yourmouseisadatabaseofmousemovesandclicks.
IntheworldofRx,suchasynchronousdatasourcesarecomposedusingvariouscombinatorsintheLINQsense,allowingthingslikefilters,projections,joins,time-basedoperations,etc.
LabflowInthislab,we'llexploreRxinagradualmanner.
First,we'llhavealookatthecoreobjectsofRxwhichshipinaspartoftheReactiveExtensionsforJavaScript.
Onceweunderstandthoseobjects,we'llmoveontoshowhowtheRxlibrariesallowforcreatingsimpleobservablesequencesusingfactorymethods.
Thisallowsforsomebasicexperimentation.
Asweproceed,we'llintroducehowtobridgewithexistingasynchronouseventsourcessuchasDOMeventsandtheasynchronouspattern.
Showingmorequeryoperatorsaswemovealong,we'llendupatapointwherewestarttocomposemultipleasynchronoussources,unleashingthetruepowerofRx.
Exercise1–GettingstartedwithRxinterfacesandassembliesObjective:IntroducingthecoreRxnotionsofobservablecollectionsandobservers,whicharereflectedinseveralobjectdefinitionsintheRxJSlibrary.
Thosearemirrorimagesofthetwonew.
NET4interfacesSystem.
IObservableandSystem.
IObserver.
1.
OpenVisualStudio2010andgotoFile,New,Project…tocreateanewASP.
NETWebApplicationprojectinC#.
Makesurethe.
NETFramework4targetissetinthedropdownboxatthetopofthedialog.
2.
InordertogetstartedwithRxJS,weneedtoaddareferencetothelibrarywhichgotinstalledonthelabmachineintheC:\ProgramFiles\MicrosoftCloudProgrammability\ReactiveExtensions\v1.
0.
2590.
0\RX_JSfolder.
Right-clickontheScriptsfolderofyourwebproject,andchooseAdd,ExistingItem…toaddallofthe.
jsfilesinthefoldermentionedhere.
TheProjectshouldlookasfollows:Note:WhendownloadingRxJSfromtheDevLabssite,asingleZIPfilewillcontainthesamefilesastheonesthatappearinthisfolder.
3.
Tostarttheexplorationandtokeepthingsassimpleaspossible,addanewHTMLtotheprojectusingtheAdd,NewItem…dialog:It'sconvenienttosetthispageasthestartpageforaneasyF5debuggingexperience:4.
Next,we'llimportthescriptfileintotheHTMLpageusingascripttag.
Thefollowingmarkupisusedtoachievethis:IntroductoryExercise5.
Nowwe'rereadytostartexploringtheRxJSlibrary.
Addanotherscripttagwithtypetext/javascriptandaddafunctiontoit.
Atthispoint,wewon'texecutethescriptcodeyetbutsimplylookatVisualStudio'sIntelliSenseprovidedfortherx.
jsfile.
Inthescreenshotabove,we'retypingRx.
ObstoseeIntelliSensecompletethevariousobjectsthathavebeenaddedtothetop-level"Rx"declaration.
Ofthose,thekeyonesareObservableandObserver.
Let'shaveacloserlooknow.
6.
Firstoff,anobservableisacollectionthatcannotifysubscribedobserversaboutnewvalues.
ThisiswheretheessenceofasynchronousprogramminginRxliesin.
Byrepresentingasynchronouscomputationsascollections,awholebunchofoperatorscanbedefinedoverthose.
We'llcoverthisaspectlater.
Fornow,let'shavealookatwhattheObservableobjecthastooffer:Whatyou'reseeinghereareabunchofoperatorsthatdon'toperateonaninstanceofanobservable.
Forthosewho'reintoC#andothermanagedlanguages,thefunctionsshownhereroughlycorrespondtostaticmethods(excludingextensionmethods).
Inthenextexercise,we'llexplorehowtocreateobservablesequencesusingsomeofthosemethodsthatactasfactories.
Note:Inlanguagesdesignedbypeoplewhocareabouttypes(muchlikeoneoftheauthors),Rxusestwointerfacestocapturethenotionofanobservableandanobserver.
ThosearecalledIObservableandIObserver,respectively.
Startingwith.
NET4,bothinterfacesarebuiltintomscorlib.
dll.
On.
NET3.
5andintheSilverlightflavorsofRx,aseparateassemblycalledSystem.
Observable.
dllhastobeincludedintheprojectinordertogetaccesstothosetwointerfaces.
Fornow,justenterthefollowingcodetocreateoneofthemostbasicobservablesequences.
Allitdoesislettingitsobserversknowaboutasinglevaluethat'sbecomeavailable:varanswer=Rx.
Observable.
Return(42);Withthisobservable(single-element)sequenceinplace,wecanhavealookattheequivalentofinstancemethodsinlanguageslikeC#.
Let's"dotinto"theanswerobjectandseewhat'savailable:LINQ-savvyreaderswillimmediatelyrecognizesomeoftheStandardQueryOperators,butwe'llskipoverthisaspectfornow.
ThehighlightedSubscribefunctioniswhat'simportanttohaveasolidunderstandingaboutrightnow.
Contrasttoenumerablesequences,observablesequencesdon'thaveaGetEnumeratorfunction.
Wheretheuseofanenumeratoristopulldatatoyou("Hey,givemethenextelementpleaseGotmore"),apush-basedmechanismisdesiredforobservablesequences.
So,insteadofgettinganenumerator,youhavetogiveanobservertoanobservablesequence.
Thisobserverwillthenbeusedtonotifyyouabouttheavailabilityofnewdata,e.
g.
whenanasynchronouswebservicecallcompletesoraneventwasraised.
Let'shaveacloserlookattheSubscribefunctionnow:Subscribecantakeuptothreeparameterswhichrepresenttheobserver.
Theroleofanobserveristoreceivenotificationsfromtheobservablesequence.
Threesuchmessagesexistascanbewitnessedbythefollowingexperiment.
Let'screateanObserverusingtheObserver.
Createfunction:varobserver=Rx.
Observer.
Create(function(x){document.
write("Theansweris"+x);});WhiletypingtheCreatemethodyouwillhavenoticedthereareuptothreeparameterssupported.
Intheabove,we'veonlyspecifiedone.
ThisparticularoneactsastheOnNexthandlerwhichwillgetcalledbyanobservablesequencewhendataisavailable.
Forexample,ifwegivetheobservertothe"answer"sequence,acallwillbemadetotheOnNextfunctionshownabove.
Twootherfunctionsexist:We'llexploretheroleofOnCompletedandOnErrorinthenextexercise,butsufficeittosaythatthosefunctionsactascallbacksusedbyanobservablesequence.
IntheCreatecallabove,we'vesimplyomittedeverythingbuttheOnNexthandler.
Nowwecanpasstheobservertotheobservable"answer":answer.
Subscribe(observer);Note:Duetosizeminimizationoftherx.
jslibrary,parameternamesdon'tlookverymeaningful.
Lookingatthe.
NETversionofthecorrespondinginterfacewouldrevealtheintentofthoseparameters.
WhileIntelliSensecanbeprovidedforJavaScriptlibrariesinVisualStudio2008andbeyond,wehaven'tdoneso(yet).
OnsourceofadditionalcomplexityinvolvedinthisistheabsenceofoverloadsinJavaScript.
Infact,wedosupportvariousoverloadsforabunchoffunctionsbymeansofparameterchecks.
HowthiswouldlookinIntelliSenseisn'tquiteclearatthispoint.
7.
Sofar,we'vewrittenthefollowingpieceofcodethatweshallrecapbriefly.
Anobservablesequenceisanobjectthatwillproducevaluesinanasynchronousmanner.
Inorderforittosignaltheavailabilityofsuchvalues,itusesanobservertomakecallbacksto.
Threesuchcallbacksexist,whicharesubjectofthenextexercise,butfornowwe'veonlyspecifiedtheOnNexthandlerwhichwillreceivetheobservable'svalues:functioniExploreRx(){varanswer=Rx.
Observable.
Return(42);varobserver=Rx.
Observer.
Create(function(x){document.
write("Theansweris"+x);});answer.
Subscribe(observer);}Inordertoshowthiscodeinaction,let'shookuptheiExploreRxfunctiontoabutton.
Laterwe'llusejQuerytofacilitateeasierconfigurationofeventhandlersandsoon:TellmetheanswerClickingthebuttonwillprint"Theansweris42"onthescreen,asillustratedbelow.
Note:Useofthedocument.
writeanddocument.
writelnfunctionsisquiteinvasiveasitdoesn'tappendthecontenttotheexistingdocument.
Beyondthisintroductoryexample,we'llusemoresophisticatedHTMLandJavaScriptfacilitiestoputresponsesonthescreen.
ThereadermayrecallthatSubscribedidn'tjusttakeonebutapossibletotalofthreeparameters.
WhataretheremainingtwoforWell,insteadofhavingyoucreateanobservermanuallyusingtheObserver.
Createfunction,onecanpassthetripletofcallbackfunctions(orcertainsubsetsthereof)directlytotheSubscribefunction:varanswer=Rx.
Observable.
Return(42);answer.
Subscribe(function(x){document.
write("Theansweris"+x);});Othercallbackfunctionswillbediscussedinthenextexercise.
Thereadershouldrunthemodifiedcodetoensurethesameresultisproduced,thistimewithlesscodingeffort.
8.
Onethingwehaven'tquitetalkedaboutyetisthereturnvalueofaSubscribefunctioncall.
Rxdiffersfromothereventmodels(likeclassic.
NETevents)inthewayunsubscription(ordetachmentofahandlerifyouwill)isaccomplished.
Insteadofrequiringtheuseofanotherconstructtounsubscribe,thereturnvalueofSubscriberepresentsahandletothesubscription.
Inordertounsubscribe,onecallsDisposeonthereturnedobject:FortrivialsequencesliketheoneconstructedusingReturn,theuseofDisposeisn'tapplicableoften.
However,whenbridgingwithongoingeventstreams–e.
g.
DOMeventsasweshallseelater–thisfunctionalitycomesinhandy.
Conclusion:ObservableandObserverobjectsrepresentadatasourceandalistener,respectively.
Inordertoobserveanobservablesequence'snotifications,onegivesitanobserverobjectusingtheSubscribefunction,receivingahandleobjectthatbeusedtounsubscribebycallingDispose.
Inordertogainaccesstothoseobjectsandfunctions,onehastoimporttherx.
jsscriptlibraryasdiscussedhere.
Exercise2–CreatingobservablesequencesObjective:ObservablesequencesarerarelyprovidedbyimplementingtheIObservableinterfacedirectly.
Insteadawholesetoffactorymethodsexistthatcreateprimitiveobservablesequences.
Thosefactorymethodsprovideagoodmeansforinitialexplorationofthecorenotionsofobservablesourcesandobservers.
1.
EnsuretheprojectsetupofExercise1isstillintact,i.
e.
containingthereferencestoourJavaScriptfiles.
Alsoensurethefollowingskeletoncodeisputinplace.
Herewe'llexploreOnErrorandOnCompletedaswell:varsource=null;//We'llexploresomefactorymethodsherevarsubscription=source.
Subscribe(function(next){$("").
html("OnNext:"+next).
appendTo("#content");},function(exn){$("").
html("OnError:"+exn).
appendTo("#content");},function(){$("").
html("OnCompleted").
appendTo("#content");});2.
We'llstartbylookingattheEmptyfactorymethod:varsource=Rx.
Observable.
Empty();Runningthiscodeproducesthefollowingoutput:OnCompletedInorderwords,theemptysequencesimplysignalscompletiontoitsobserversbycallingOnCompleted.
ThisisverysimilartoLINQtoObject'sEnumerable.
Emptyoranemptyarray.
Forthoseenumerablesequences,thefirstcalltotheenumerator'sMoveNextmethodwouldreturnfalse,signalingcompletion.
Background:Onemaywonderwhenobservablesequencesstartrunning.
Inparticular,what'striggeringtheemptysequencetofireouttheOnCompletedmessagetoitsobserversTheanswerdiffersfromsequencetosequence.
Mostofthesequenceswe'relookingatinthisexerciseareso-calledcoldobservableswhichmeanstheystartrunninguponsubscription.
Thisisdifferentfromhotobservablessuchasmousemoveeventswhichareflowingevenbeforeasubscriptionisactive(there'snowaytokeepthemousefrommovingafterall…).
3.
BesidestheOnCompletedmessage,OnErrorisalsoaterminatingnotification,inasensenomessagescanfollowit.
WhereEmptyisthefactorymethodcreatinganobservablesequencethatimmediatelytriggerscompletion,theThrowmethodcreatesanobservablesequencethatimmediatelytriggersanOnErrormessagetoobservers:varsource=Rx.
Observable.
Throw("Oops!
");Runningthiscodeproducesthefollowingoutput:OnError:OopsBackground:TheOnErrormessageistypicallyusedbyanobservablesequence(notastrivialastheonesimplyreturnedbyacalltoThrow)tosignalanerrorstatewhichcouldeitheroriginatefromafailedcomputationorthedeliverythereof.
FollowingthesemanticmodeloftheCLR'sexceptionmechanism,errorsinRxarealwaysterminatingandexhibitafail-fastcharacteristic,surfacingerrorsthroughobserverhandlers.
MoreadvancedmechanismstodealwitherrorsexistintheformofhandlerscalledCatch,OnErrorResumeNextandFinally.
Wewon'tdiscussthoseduringthisHOL,buttheirroleshouldbeself-explanatorybasedoncorrespondinglanguageconstructsinvariousmanagedlanguages.
4.
OnefinalessentialfactorymethodorprimitiveconstructoriscalledReturn.
Itsroleistorepresentasingle-elementsequence,justlikeasingle-cellarraywouldbeintheworldofenumerablesequences.
Thebehaviorobservedbysubscribedobserversistwomessages:OnNextwiththevalueandOnCompletedsignalingtheendofthesequencehasbeenreceived:varsource=Rx.
Observable.
Return(42);Runningthiscodeproducesthefollowingoutput:OnNext:42OnCompletedBackground:ReturnplaysanessentialroleinthetheorybehindLINQ,knownasmonads.
TogetherwithanoperatorcalledSelectMany(whichwe'lllearnaboutmorelateron),theyformtheprimitivefunctionsneededtoleveragethepowerofmonadiccomputation.
Moreinformationcanbefoundbysearchingthewebusingmonadandfunctionalasthekeywords.
5.
Atthispoint,we'veseenthemosttrivialobservablesequenceconstructorsthatareintimatelyrelatedtoanobserver'stripletofmethods.
Whilethoseareofinterestincertaincases,moremeatysequencesareworthtoexploreaswell.
TheRangeoperatorisjustoneoperatorthatgeneratessuchasequence.
SymmetrictothesameoperatoronEnumerable,Rangedoesreturnasequencewith32-bitintegervaluesgivenastartingvalueandalength:varsource=Rx.
Observable.
Range(5,3);Runningthiscodeproducesthefollowingoutput:OnNext:5OnNext:6OnNext:7OnCompletedNote:Aswithallthesequencesmentionedinthisexercise,Rangeisacoldobservable.
Torecap,thissimplymeansthatitstartsproducingitsresultstoanobserveruponsubscription.
Anotherpropertyofcoldobservablesequenceisthateverysubscriptionwillcausesuchreevaluation.
Thus,iftwocallstoSubscribearemade,bothoftheobserverspassedtothosecallswillreceivethemessagesfromtheobservable.
It'snotbecausethedataobservationhasruntocompletionforoneobserverthatotherobserverswon'trunanymore.
Whetherornottheproduceddataisthesameforeveryobserverdependsonthecharacteristicsofthesequencethat'sbeinggenerated.
Fordeterministicand"puristfunctional"operatorslikeReturnandRange,themessagesdeliveredtoeveryobserverwillbethesame.
However,onecouldimagineotherkindsofobservablesequencesthatdependonside-effectsandthusdeliverdifferentresultsforeveryobserverthatsubscribestothem.
6.
Togeneralizethenotionofsequencecreationintermsofageneratorfunction,theGenerateconstructorfunctionwasaddedtoRx.
Itcloselyresemblesthestructureofafor-loopasonewouldusetogenerateanenumerablesequenceusingC#iterators(cf.
the"yieldreturn"and"yieldbreak"statements).
Todoso,ittakesanumberofdelegate-typedparametersthatexpectfunctiontocheckfortermination,toiterateonestepandtoemitaresultthatbecomespartofthesequenceandissenttotheobserver:varsource=Rx.
Observable.
Generate(0,function(i){returni").
html("OnNext:"+next).
appendTo("#content");},function(exn){$("").
html("OnError:"+exn).
appendTo("#content");},function(){$("").
html("OnCompleted").
appendTo("#content");});ThissampleusesaslightlydifferentoperatorcalledGenerateWithTimethatallowsspecifyingiterationtimebetweenproducingresults,dependentontheloopvariable.
Inthiscase,0willbeproduceduponsubscription,followedby1asecondlater,then4twosecondslater,9threesecondslaterand16foursecondslater.
Noticehowthenotionoftime–allimportantinanasynchronousworld–isenteringthepicturehere.
a.
SetabreakpointonthehighlightedlambdaexpressionbodyusingF9.
NoticeyouneedtobeinsidethelambdabodywiththecursorintheeditorinorderforthebreakpointtobesetonthebodyandnottheoutermethodcalltoSubscribe.
b.
StartrunningtheprogrambypressingF5.
Atthistime,weenduphittingourbreakpointascanbeseenfromthedebugger:c.
ThereadershouldfeelfreetohitF5acouplemoretimestoseethebreakpointgettinghitforeverysubsequentOnNextmessageflowingoutoftheobservablesequence.
SettingabreakpointontheOnCompletedwillshowthesamebehaviorfortheGenerateWithTimesequence:Note:Inthe.
NETversionofRx,theplacewherenotificationsaredeliveredisdependentonaspectsofconcurrency,whichcanbecontrolledusinganISchedulerobject.
ThisnotionisnotpresentinRxJSsinceJavaScripthasatotallydifferent–mostlynon-existent–concurrencyphilosophy.
Conclusion:CreatingobservablesequencesdoesnotrequiremanualimplementationoftheIObservableinterface,nordoestheuseofSubscriberequireanIObserverimplementation.
Fortheformer,aseriesofoperatorsexistthatcreatesequenceswithzero,oneormoreelements.
Forthelatter,SubscribeextensionmethodsexistthattakevariouscombinationsofOnNext,OnErrorandOnCompletedhandlersintermsofdelegates.
Exercise3–ImportingDOMeventsintoRxObjective:Creatingobservablesequencesoutofnowhereusingvariousfactory"constructor"methodsencounteredinthepreviousexerciseisonething.
Beingabletobridgewithexistingsourcesofasynchronyintheframeworkisevenmoreimportant.
Inthisexercisewe'lllookatthejQuerytoObservableoperatorthatallows"importing"aDOMorcustomeventintoRxasanobservablecollection.
Everytimetheeventisraised,anOnNextmessagewillbedeliveredtotheobservablesequence.
Background:Rxdoesn'taimatreplacingexistingasynchronousprogrammingmodelsinJavaScript.
TechnologieslikeDOMevents(usingjQuery),AJAXandwhatnotareperfectlysuitedfordirectuse.
However,oncecompositionentersthepicture,usingthoselow-levelconceptstendstobeagruelingexperiencedealingwithresourcemaintenance(e.
g.
whentounsubscribe)andoftenhastoreinventthewheel(e.
g.
howdoyou"filter"anevent).
Needlesstosay,allofthiscanbeveryerrorproneanddistractsthedeveloperfromtherealproblembeingsolved.
Inthissampleandtheonesthatfollow,we'llshowwherethepowerofRxcomesin:compositionofasynchronousdatasources.
1.
TheHTMLDOMisagoodsampleofanAPIthat'sfullofevents.
Startingfromourexistingwebproject,weshouldstillhavethesingleHTMLpagelyingaround.
InordertobridgewiththeDOM,we'llbeusingjQueryandRx'swrapperforit.
Thefollowingscriptreferenceshavetobeaddedtothepagetoenablethis:2.
UsingjQuery,wecanwritethefollowingcodetoattachaneventhandlertothedocument'sreadyevent:$(document).
ready(function(){});Obviously,theabovedoesn'tdomuchaswe'vespecifiedano-opfunctionforthehandler.
InordertobeabletocontrastthiskindofDOMeventhandlingfromtheRxapproachusingobservablesequences,let'sshowhookingupthemousemoveeventwithinthereadyeventhandler:$(document).
ready(function(){$(document).
mousemove(function(event){//Apositiontrackingmechanism,updatingaparagraphcalled"content"$("").
text("X:"+event.
pageX+"Y:"+event.
pageY).
appendTo("#content");});});Togettheabovetowork,addacontenttagtothepage:Whilethisworksgreat,thereareanumberoflimitationsassociatedwithclassicDOMevents:Eventsarehiddendatasources.
Itrequireslookingatthehandler'scodetoseethis.
DidyoueverregardthemousemoveeventasacollectionofpointsIntheworldofRx,weseeeventsasjustanotherconcreteformofobservablesequences:yourmouseisadatabaseofpoints!
Eventscannotbehandedout,e.
g.
aneventproducingPointvaluescannotbehandedouttoaGPSservice.
Thedeeperreasonforthisisthateventsarenotfirst-classobjects.
IntheworldofRx,eachobservablesequenceisrepresentedusinganobjectthatcanbepassedaroundorstored.
Eventscannotbecomposedeasily.
Forexample,youcan'thireamathematiciantowriteagenericfilteroperatorthatwillfilteranevent'sproduceddatabasedonacertaincriterion.
IntheworldofRx,duetothefirst-classobjectnatureofobservables,wecanprovidegenericoperatorslikeWhere.
Eventsrequiremanualhandlermaintenancewhichrequiresyoutorememberthefunctionthatwashandedtoit.
It'slikekeepingyourhandsonthemoneyyoupaidforyournewspapersubscriptioninordertobeabletounsubscribe.
IntheworldofRx,yougetahandletounsubscribeusingDispose.
3.
Nowlet'sseehowthingslookwhenusingRx.
ToimportaneventintoRxusingjQuery,weusethetoObservableoperator,whichwetellthejQueryEventobjectthatwillberaisedbytheeventbeingbridged:$(document).
ready(function(){$(document).
toObservable("mousemove").
Subscribe(function(event){//Apositiontrackingmechanism,updatingaparagraphcalled"content"$("").
text("X:"+event.
pageX+"Y:"+event.
pageY).
appendTo("#content");});});We'llnowexplainthisfragmentindepthtodrivehomethepointswemadebefore:ThetoObservableoperatorturnsthegivenevent–specifiedasastring–inanobservablesequencewithajQueryEventobject.
WhencallingSubscribe,ahandlerisattachedtotheunderlyingevent.
Foreverytimetheeventgetsraised,thejQueryEventobjectissenttoalloftheobservable'ssubscribers.
InsideourOnNexthandler,wecangetthemousepositionviathepageXandpageYproperties.
ItgoeswithoutsayingthatthosepropertiescouldbewrappedinaJSONobjectrepresentingapoint.
Ifdesired,clean-upoftheeventhandlercanbetakencareofbycallingDisposeontheobjectreturnedbythetoObservableoperator.
4.
Tomasterthetechniqueabitfurther,let'shavealookatanotherDOMevent.
FirstaddaninputelementtoourHTMLpage.
RestructurethecodetolookasfollowsinordertohaveboththeDOMdocument'smousemoveandtheinput'skeyupeventhookedup.
We'llwriteoutputtothecontentelementasaformoflogging.
InExercise5we'lllearnaboutaspecializedoperator(calledDo)thatcanbeusedforthispurpose.
$(document).
ready(function(){$(document).
toObservable("mousemove").
Subscribe(function(event){$("").
text("X:"+event.
pageX+"Y:"+event.
pageY).
appendTo("#content");});$("#textbox").
toObservable("keyup").
Subscribe(function(event){$("").
text("Userwrote:"+$(event.
target).
val()).
appendTo("#content");});});Atthispointitmayseemwehaven'tgainedtoomuchyet.
Thingsarejust"different".
Whatreallymattersthoughisthatwe'veputusintheworldofObservableobject,overwhichalotofoperatorsaredefinedthatwe'lltalkaboutinamoment.
Foronething,noticeallthehoopsonehastogothroughinordertogetthetextoutofakeyupevent:getthetargetandcallaval()function.
Asmentionedbefore,classicDOMeventsdon'texplicitlyexhibitadata-orientednature.
Thisparticulareventisagreatexampleofthisobservation:fromtheeventhandlerofakeyupeventonedoesn'timmediatelygetthetextafterthechangehasoccurred,eventhoughthat'swhat99%ofusesoftheeventareabout(theother1%maybejustifiedby"stateinvalidationhandling",e.
g.
toenable"Doyouwanttosavechanges"behavior).
UsingoperatorslikeSelectwecansimplifythiscode,aswe'llseeinthenextexercise.
5.
Afragmentofsampleoutputisshownbelow:Conclusion:DOMeventsarejustoneformofasynchronousdatasources.
Inordertousethemasobservablecollections,RxprovidesthejQueryoperatortoObservable.
InreturnonegetsEventobjectscontainingthejQueryeventinformation.
Exercise4–AfirstlookatsomeStandardQueryOperatorsObjective:Lookingatobservablesequencesasasynchronousdatasourcesiswhatenablesthemtobequeried,justlikeanyotherdatasource.
WhosaysqueryinginthecontextofC#programmingnowadays,immediatelythinksLINQ.
Inthisexercisewe'llshowhowtousetheLINQsyntaxtowritequeriesoverobservablecollections.
1.
Continuingwiththepreviousexercise'scode,let'shavealookbackatthecodewewrotetohandlevariousUI-relatedeventsbroughtintoRxusingthejQuerytoObservableoperator:$(document).
toObservable("mousemove").
Subscribe(function(event){$("").
text("X:"+event.
pageX+"Y:"+event.
pageY).
appendTo("#content");});$("#textbox").
toObservable("keyup").
Subscribe(function(event){$("").
text("Userwrote:"+$(event.
target).
val()).
appendTo("#content");});RecallthemovesandinputcollectionscontainjQueryEventobjects.
Quiteoftenwe'renotinterestedinalloftheinformationanEventobjectcapturesandwantto"shakeoff"redundantstuff.
2.
InaclassicjQueryeventworld–andinanyotherprogrammingmodelforasynchronousdatasourcesbeforetheadventofRxforthatmatter–suchdata-intensiveoperationsoftenledtoimperativecode.
Forevents,manyofyouwilllikelyhavewrittencodelikethis:functionhandleMouseMove(event){if(event.
pageX===event.
pageY){//Onlyrespondtoeventsformousemovesoverthefirstbisectorofthepage.
}}functionhandleKeyUp(event){vartext=$(event.
target).
val();//Andnowwecanforgetabouttherestoftheeventobject'sdata.
.
.
}Whatwe'vereallydonehereismimickingdata-intensive"sequenceoperators"inanimperativeway.
Thefirstsampleshowsafilterusinganif-statement;thesecondoneembodiesaprojectionusinganotherlocalvariable.
Indoingso,we'velostanimportantpropertythough.
Thepartsomittedbygreencommentsnolongerdirectlyoperateonaneventbutarelostinaseaofimperativecode.
Inotherwords,it'snotpossibletofilteraneventandgetanothereventback.
3.
InthebravenewRxworld,wecandobetterthanthis.
Sinceobservablesequenceshavegottenafirst-classstatusbyrepresentingthemasObservableobjects,wecanprovideoperatorsoverthembyprovidingasetoffunctions.
Let'shavealookathowwe'drevampourimperativeeventhandlercodeusingthosequeryoperatorsoverobservablesequences.
First,let'sturntheevent-basedinputsequencesintowhatwewishthey'dlooklike:varmoves=$(document).
toObservable("mousemove").
Select(function(event){return{pageX:event.
pageX,pageY:event.
pageY};});varinput=$("#textbox").
toObservable("keyup").
Select(function(event){return$(event.
target).
val();});varmovesSubscription=moves.
Subscribe(function(pos){$("").
text("X:"+pos.
pageX+"Y:"+pos.
pageY).
appendTo("#content");});varinputSubscription=input.
Subscribe(function(text){$("").
text("Userwrote:"+text).
appendTo("#content");});UsingtheSelectfunction,weprojectawaytheEventobjectinfavorofaJavaScriptobjectwithpointsandastring,respectively.
Asaresultboththemovesandinputsequencesnowareobservablesequencesofameaningfuldatatypethatjustcaptureswhatweneed.
Background:Theabilitytorepresentasynchronousdatasourcesasfirst-classobjectsiswhatenablesoperatorslikethistobedefined.
Beingabletoproduceanobservablesequencethatoperatesbasedoninputofoneormoresequencesisnotjustinterestingfromadatapointofview.
Equallyimportantistheabilitytocontrolthelifetimeofsubscriptions.
Considersomeonesubscribesto,say,theinputsequence.
WhatreallyhappenshereisthattheSelectoperator'sresultsequencegetsarequesttosubscribeanobserver.
Onitsturn,itpropagatesthisrequesttoitssource,whichisasequenceproducedbytoObservable.
Ultimately,theevent-wrappingsourcesequencehooksupaneventhandler.
Disposingasubscriptionispropagatedinasimilarmanner.
4.
Withtheprojectionsinplacetoreducethenoiseoninputsequences,wecannoweasilyfilterthemousemovestothoseoverthefirstbisector(wherexandycoordinatesareequal).
HowdoyouperformafilterinLINQUsetheWhereoperator:varoverFirstBisector=moves.
Where(function(pos){returnpos.
pageX===pos.
pageY;});varmovesSubscription=overFirstBisector.
Subscribe(function(pos){$("").
text("Mouseat:"+pos.
pageX+","+pos.
pageY).
appendTo("#content");});ThetypeforbothmovesandoverFirstBisectorwillbeanobjectwithapageXandpageYproperty.
5.
Asampleoftheoutputisshownbelow.
Alloftheemittedmousemovemessagessatisfythefilterconstraintwespecifiedinthequery:Conclusion:Thefirst-classnatureofobservablesequencesasObservableobjectsiswhatenablesustoprovideoperators(sometimesreferredtoascombinators)tobedefinedoverthem.
Themajorityofthoseoperatorsproduceanotherobservablesequence.
Thisallowscontinuous"chaining"ofoperatorstomanipulateanasynchronousdatasource'semittedresultstilltheapplication'srequirementsaremet.
Otherswillbediscussedfurtheron.
Exercise5–Morequeryoperatorstotametheuser'sinputObjective:Observablesequencesareoftennotwell-behavedfortheintendedusage.
Wemaygetdatapresentedinoneformbutreallywantitinanothershape.
Forthis,simpleprojectioncanbeusedasshowninthepreviousexercise.
Buttherearefarmorecasesofill-behaveddatasources.
Forexample,duplicatevaluesmaycomeout.
Butthere'smorebeyondtheperspectiveofobservablesequencesas"justdatasources".
Inparticular,asynchronousdatasourceshaveanintrinsicnotionoftiming.
WhatifasourcegoestoofastforconsumerstodealwiththeirdataWe'lllearnhowtodealwiththosesituationsinthisexercise.
Fromthisexerciseon,we'llbefloatingonacommonthemeofthetypical"dictionarysuggest"sampleforasynchronousprogramming.
Theideaistolettheusertypeaterminaninputelementandshowallthewordsstartingwiththeterm,byconsultingawebservice.
TokeeptheUIfromfreezing,wegottodothiskindofstuffinanasynchronousmanner.
Rxisagreatfitforthiskindofcomposition.
Butfirstthingsfirst,let'sseehowtheinputelementisbehaving.
1.
Inwhatfollows,wewon'tneedthemousemoveeventanymore,solet'sstickwithjustasingleinputelementandits(projected)keyupevent:varinput=$("#textbox").
toObservable("keyup").
Select(function(event){return$(event.
target).
val();});varinputSubscription=input.
Subscribe(function(text){$("").
text("Userwrote:"+text).
appendTo("#content");});2.
Now,let'scarryoutafewexperiments.
Loadthewebpageandtype"reactive"(withoutthequotes)intheinputbox.
Obviouslyyoushouldseenolessthaneighteventsbeinghandledthroughtheobserver.
However,noticewhathappensifyouselectasingleletterinthewordandoverwriteitbythesameletter:react|ive(SHIFT-LEFTARROW)reactive(typet)react|iveThescreenshotbelowshowsthecorrespondingoutput.
What'sthisduplicatemessageattheendaboutDidn'tweaskforkeyupeventsYes,butitturnsouttheDOMdoesnotkeeptrackofthelastvalueenteredbytheuserandmayraisefalsepositivesbasedoninternalstatechanges.
Noticethesame"issue"appearswhenpastingthesametextovertheentireselectionoftheinput(e.
g.
CTRL-A,CTRL-C,CTRL-Vtoexhibitthequirk).
3.
Assumeforamomentwetaketheuserinputandfeedittoadictionarysuggestwebservice(aswewilllateron)whichchargestheuserorapplicationvendorforeveryrequestmadetotheservice.
Doyoureallywanttopaytwicethepricetolookup"reactive"becauseofsomeweirdbehaviorintheDOMLikelynot.
SohowwouldyousolvetheissueinanRx-freeworldWell,you'dkeepsomestatesomewheretokeeptrackofthelastvalueseenandonlypropagatetheinputthroughincaseitdiffersfromthepreviousinput.
Allofthiscluttersthecodesignificantlywiththingslikeaprivatefield,anif-statementandadditionalassignmentintheeventhandler,etc.
Butworse,allthelogicgoesinaneventhandlerwhichlackscomposition:atnopointwehaveanasynchronousdatasource,freeofduplicates,wecanputourhandson(e.
g.
tohandittoanothercomponentinthesystem).
InRx,thankstothepowerofcomposition,wegetawaywithasingleoperatorcallthatdoesallthecomparisonandstatemaintenanceonourbehalf.
ThisoperatoriscalledDistinctUntilChanged:varinput=$("#textbox").
toObservable("keyup").
Select(function(event){return$(event.
target).
val();}).
DistinctUntilChanged();varinputSubscription=input.
Subscribe(function(text){$("").
text("Userwrote:"+text).
appendTo("#content");});Withthisfixinplace,runsofidenticalvalueswillonlycausethefirstsuchvaluetobepropagated.
Ifthevaluereceivedfromthesourceisdifferenttheveryfirstvalueordifferentfromthepreviousone,itgoesoutatthatverymoment.
Ifit'sthesameasthepreviousvalue,it'smutedandattachedobserversdon'tgettoseeit.
Background:It'sessentialtounderstandhowdataflowsthrougha"network"ofinterconnectedobservablesequences.
Inthesampleabove,therearethreesequencesinthemix.
First,there'stoObservablethatlistensonaDOMeventandemitsitsvaluestosubscribedobservers.
Next,theSelectoperatortakescareofcarryingoutaprojectionbyreceivingvalues,transformingthemandsendingthemout.
Finally,DistinctUntilChangedreceivesoutputfromSelect,filtersoutconsecutiveduplicatesandpropagatestheresultstoitsobserver.
Thefigurebelowillustrateshowasubscriptionissetupandhowdataflowsthroughtheoperators.
Note:NoticethecasingofoperatorsinRxJS.
WhiletypicallyfunctionsinJavaScriptarespelledwithalowercaselettertostartwith,RxJSdoesn'tfollowthisconvention.
Thereareacoupleofreasonsforthis.
Someoperators,namelytheonesforimperative-styleasynchronouscomputationlikeIfandWhere,conflictwithkeywordsofthelanguage.
AnotherreasonisconsistencywithRx.
NET,makingportingcodebackandfortheasier.
Eachoperatorisalittleblackboxthatknowshowtopropagatesubscriptionstoitssourcesequence(s),aswellashowtotakethedataitreceivesandtransformittosenditalong(ifdesired).
Allofthisworksnicetillthepointyouseesomedatacomingoutintheobserverandwonderwhereitcomesfrom.
Tofigurethatout,ahandyoperatoriscalledDo,whichallowstologthedatathat'sflowingthrougha"wire":ThisistheRxformof"printfdebugging".
TheDo-operatorhasseveraloverloadsthataresimilartoSubscribe's.
Eitheryougiveitanobservertomonitorthedatathat'sbeenpropagateddownthroughtheoperatororyoucangiveitasetofhandlersforOnNext,OnErrorandOnCompleted.
Note:TheDooperatorisalsoagreatwaytolearnRx.
Simplyobservetheflowofdatausingit,forexamplebyloggingnotificationstoaparagraphelementinHTML.
DuetotheabsenceofLINQsyntaxinJavaScript,it'sveryeasytoinsertaDooperatorbetweentwoconsecutiveoperatorapplications,whicharerealizedusingchainsofmethodscallsaswe'veseenabove.
InC#withRx.
NET,useofDocanbealittlemoreintrusivewhenqueryexpressionkeywordsareusedsinceoneneedstoswitchbackto(extension)methodcallingsyntaxinordertowireupaDooperator(e.
g.
between"where"and"select").
Thissaid,theauthorwouldlovetoseeLINQ-alikesyntaxinlanguagesotherthanC#andVisualBasic.
Belowisasampleoftheoperator'susetoseeDistinctUntilChangedfilteringouttheduplicatevaluesitreceives:varinput=$("#textbox").
toObservable("keyup").
Select(function(event){return$(event.
target).
val();}).
Do(function(text){$("").
text("BeforeDistinctUntilChanged:"+text).
appendTo("#content");}).
DistinctUntilChanged();varinputSubscription=input.
Subscribe(function(text){$("").
text("Userwrote:"+text).
appendTo("#content");});Beforetheuser'sinputissentontoDistinctUntilChanged,welogittothecontentelement.
ThefinalresultreceivedbySubscribe–havingflownthroughtheDistinctUntilChangedoperator–isalsologgedtothesameelement.
Laterwe'llfeedthisdataintowebservicecalls.
Withthisinplace,theoutputlooksasfollows.
Hereweproducedthequirkyduplicateinputafewtimesandyetthe"Userwrote"messageonlyappearsforthefirstsuchinput.
4.
Backtoourrunningsample,there'syetanotherproblemwiththeuser'sinput.
Sincewe'llultimatelyfeedittoawebservice(whichmaybe,aswesaidbefore,"payforplay"onaper-requestbasis),it'sunlikelywewanttosendrequestsforeverysubstringtheuserwrotewhileenteringinput.
Statedotherwise,weneedtoprotectthewebserviceagainstfasttypists.
Rxhasanoperatorthatcanbeusedto"calmdown"anobservablesequence,calledThrottle:varinput=$("#textbox").
toObservable("keyup").
Select(function(event){return$(event.
target).
val();}).
Throttle(1000).
DistinctUntilChanged();varinputSubscription=input.
Subscribe(function(text){$("").
text("Userwrote:"+text).
appendTo("#content");});Thewaythisworksisatimerisusedtoletanincomingmessageageforthespecifiedduration,afterwhichitcanbepropagatedfurtheron.
Ifduringthistimeframeanothermessagecomesin,theoriginalmessagegetsdroppedonthefloorandsubstitutedforthenewonethateffectivelyalsoresetsthetimer.
Foroursample,iftheusertypes"reactive"withouthiccups(i.
e.
notwoconsecutivechangesarefurtherapartthan1second),nointermediatesubstringswillbepropagated.
Whentheuserstopstyping(afterhitting'e',causingthelastchangedeventhigherup),ittakesonesecondbeforetheinput"reactive"ispropagateddown.
Lateronewe'llfeedthisentiresequencetoawebservicewhichnowcannotbecalledexcessivelyduetoatypistgoneloose.
Toillustratetheoperator'seffect,let'susetheDooperatorinconjunctionwithtwospecializedprojectionoperatorscalledTimestampandRemoveTimestamp.
TheformertakesanObservableandturnsitintoanObservablewhichyieldsanobjectwithaValueandTimestampproperty,wherethelatterdoestheopposite.
Thoseoperatorssimplyaddorremoveatimestampatthepointamessageisreceived.
Thisallowsustovisualizetiminginformation:varinput=$("#textbox").
toObservable("keyup").
Select(function(event){return$(event.
target).
val();}).
Timestamp().
Do(function(inp){vartext="I:"+inp.
Timestamp+"-"+inp.
Value;$("").
text(text).
appendTo("#content");}).
RemoveTimestamp().
Throttle(1000).
Timestamp().
Do(function(inp){vartext="T:"+inp.
Timestamp+"-"+inp.
Value;$("").
text(text).
appendTo("#content");}).
RemoveTimestamp().
DistinctUntilChanged();Note:WeneedtoremoveandreapplythetimestamparoundtheThrottleoperatorcall.
Ifwewouldn'tdoso,Throttlewouldsimplypropagatetheoriginaltimestampedvalue.
ThistechniqueallowsustoseetherealdeltabetweenenteringThrottleandleavingit,whichshouldbeabout1second(giveortakeafewmilliseconds).
Aswe'veusedthesamethreeoperatorstwice,agoodexerciseistoextractthepatternintoaspecializedoperator.
Creatingyourownoperatorsshouldn'tbehardasyoucansee:Rx.
Observable.
prototype.
logTimestampedValues=function(onNext){returnthis.
Timestamp().
Do(onNext).
RemoveTimestamp();};Belowistheoutputforthesampleoftyping"reactive"withamildhiccupafter"re"andafter"reac",bothofwhichwereinthesub-secondrange,hencenotcausingpropagationtobeyondtheThrottleoperator.
However,whentheuserstoppedtypingittook996msbeforethe"reactive"stringwasobservedintheDooperatoraftertheThrottleoperator(timersandtimingvaluesaresubjecttosomedeviationasusual).
Note:It'sstronglyencouragedtobrainstormforamomenthowyou'dwriteaThrottleoperatoronclassicDOMeventstakingallcomplexitiesoftimers,subscriptions,resourcemanagement,etc.
intoaccount.
OneofthecorepropertiesofRxisthatitallowsreusableoperatorstobewritten,operatingonawiderangeofasynchronousdatasources.
Thisimprovessignal-to-noiseratioofusercodesignificantly.
Inthisparticularsample,justtwooperatorshadtobeaddedinordertotametheinputsequencebothforitsdataandforitstimingbehavior.
Conclusion:Thankstothefirst-classnatureofobservablesequenceswewereabletoapplyoperatorstotheinputelementdatasourcestotameit.
WelearnedhowtofilteroutconsecutiveduplicatevaluesandhowtocalmdownaneventstreamusingtheThrottleoperator.
WealsointroduceddebuggingtechniquesusingDoandTimestampinthisexercise.
Mannedwithawell-behavedasynchronousinputsequencefromaninputelement,we'renowreadytowalkuptothedictionarysuggestwebservice,askforwordsuggestionsandpresentthemtotheuser.
ItgoeswithoutsayingthatRxwilloncemorebetheprotagonistinthiscompositionplay.
Butbeforewedoso,let'stalkaboutsynchronization.
Exercise6–BridgingtheasynchronousmethodpatternwithRxObjective:Inexercise3,welearnedhowtobringDOMeventstoRxbymeansofthetoObservablejQueryoperator.
OtherasynchronousdatasourcesexistinJavaScript,astestifiedbytheplethoraofasynchronousmethodpatternssuchasAJAXAPIs.
We'llnowexplorehowtoexposesuchasynchronousdatasourcesasobservables.
1.
Inourrunningsample,we'rebuildingupasimpleWikipediaautocompleteapplication.
Upontheuserenteringasearchterm,theapplicationwillfireoffacalltoawebservicetogetwordsuggestionsbackfromWikipedia.
Sincewedon'twanttoblocktheUI,we'llwanttokeepthecommunicationwiththedictionaryserviceasynchronoustoo.
Forthisexample,we'llbeusingtheEnglishversionofWikipedia(http://en.
wikipedia.
org/)andtheassociatedMediaWikiAPI(http://en.
wikipedia.
org/w/api.
php).
ForourexperimentwewillusetheopensearchAPIwhichallowsustosendsearchtermsandretrievetheresultsinaJSONobjectarray.
Totestwhatthedatawilllooklike,wecanenterthefollowingURLintoourbrowser(http://en.
wikipedia.
org/w/api.
phpaction=opensearch&search=react&format=json)whichwillsearchforthetermreactandreturntheresultsinJSONformat.
Thedatashouldlookasfollows:Thisresponsebasicallyconsistsofanarrayofstringvalues,correspondingtowhattheuserwrote.
Ultimatelywewanttowireuptherequesttothewebserviceusingtheinputelementasthesource.
Beforewegothere,let'sgothroughthemotionsofexposingthewebserviceasanobservablecollection.
2.
Keepingourexistingcodewiththeinputelement,we'llfirstfocusonbridgingwiththewebAPI.
Toperformthoseexperiments,commentoutyourcurrentcodetohaveacleandocument.
readyarea.
We'llfocusonusingthejQueryAJAXAPIandinparticular,the$.
ajaxfunction.
Belowisanexcerptofthesamplesintheajaxfunction'sdocumentation,whichcanbefoundathttp://api.
jquery.
com/jQuery.
ajax:$.
ajax({type:"POST",url:"some.
php",data:"name=John&location=Boston",success:function(msg){alert("DataSaved:"+msg);}});Inthissample,aPOSTrequestwillbemadetothegivenURL,passingitsomedata.
Uponsuccess,thespecifiedfunctionwillbecalledpassinginthereceiveddatainthemsgparameter.
Besidesasuccessfunction,anerrorhandlercanbespecifiedtoo.
Note:LookingatthisAPI,thereadershouldimmediatelyseetheasynchronousnatureofAJAXreflectedinthefunction'sparameters.
Whilethecallisbeingmade,theajaxfunctiondoesn'tblockyou.
However,whendatabecomesavailable,it'spassedtothecontinuationfunction.
OneshouldalsostarttoseetherelationshipwithRx,wherethosecontinuationfunctionsarewrappedintheconceptofanobserver.
Sincetherequesttotheopensearchservicegoescross-domain,we'llneedtoutilizetheJSONPdatatypeontheajaxfunction.
JSONPstandsfor"JSONwithpadding"andinjectsapieceofscriptintotheHTMLDOMusedtoevaluatethereturnedJSONdata.
MoreinformationonJSONPcanbefoundonthejQuerywebsite.
Allwe'llhavetodohereistospecifyadataTypeparameteronthecalltotheajaxfunction,asshowninthenextstep.
3.
Nowthatwehaveabasicunderstandingoftheajaxfunction,let'sgothroughhowwewouldcalltheAPIpassinginasearchtermofreact.
OnasuccessfulcalltotheAPI,wewillemptyanunorderedlistandthenaddlineitemsforeachentryintheresultingJSONarray.
Ifthereisafailure,wecanthenhandlethatwiththeerrorfunctionwhichpassesustheXMLHttpRequest,thetextstatusandtheerrorthrown.
Inthisinstance,we'llsimplyputtheerrorintoitsownparagraph.
$.
ajax({url:"http://en.
wikipedia.
org/w/api.
php",dataType:"jsonp",data:{action:"opensearch",search:"react",format:"json"},success:function(data,textStatus,xhr){$("#results").
empty();$.
each(data[1],function(_,result){$("#results").
append(""+result+"");});},error:function(xhr,textStatus,errorThrown){$("#error").
text(errorThrown);}});Tomakethiswork,addthefollowingtwoelementstothepage:Forcompleteness,herearetheresultsyoushouldexpecttogetback:4.
ConvertingtheabovefragmenttoRxisn'tveryhardusingtheRxprovided$.
ajaxAsObservablefunction.
Thisfunctionactsasatinywrapperaround$.
ajaxfunctionusingsuccessanderrorfunctionsthatcallintotheobserver'sOnNextandOnCompletedforthesuccesscase,andOnErrorforthefailurecase:$.
ajaxAsObservable({url:"http://en.
wikipedia.
org/w/api.
php",dataType:"jsonp",data:{action:"opensearch",search:"react",format:"json"}});NowwecanusetheSubscribefunctiontoreceivedatafromthewebservicecallforsearchterm"react".
ThisresultwillbepresentedasaJSONarray,justlikewesawinthemanualcallinstep1.
Sincewedon'twanttohardcodethesearchterm,we'llwraptheaboveinafunctiontoparameterizeinput:functionsearchWikipedia(term){return$.
ajaxAsObservable({url:"http://en.
wikipedia.
org/w/api.
php",dataType:"jsonp",data:{action:"opensearch",search:term,format:"json"}}).
Select(function(d){returnd.
data[1];});}UsingtheSelectoperatorweextracttheanswersfromtheresult.
Toseethisbitofarraytraversal,havealookbackatthefigureinstep1toseewherethestringarraywithresultsissitting.
5.
Withthisfunctioninplace,wecannowsubstitutethecodefromstep3withthefollowingRx-basedcode.
Thisshouldcontinuetoproducethesameresultsasshowninstep3:varsearchObservable=searchWikipedia("react");varsearchSubscription=searchObservable.
Subscribe(function(results){$("#results").
empty();$.
each(results,function(_,result){$("#results").
append(""+result+"");});});6.
Sincewebservicescaneasilyfail,weshouldsayawordortwoonerrorhandling.
Wedon'treallycareaboutthespecificsofpossibleerrorsbutitshouldbecommonwisdomthatintheworldofdistributedandasynchronousprogrammingerrorsarenotthatexceptional.
Rxisparticularlygoodatdealingwitherrorsduetotheseparateobserver'sOnErrorchanneltosignalthose.
Ifweweretochangeoursampleasshownbelow,theerrorwouldbehandledbytheOnErrorfunctionthat'spartoftheobserver:varsearchObservable=searchWikipedia("react");varsearchSubscription=searchObservable.
Subscribe(function(results){$("#results").
empty();$.
each(results,function(_,result){$("#results").
append(""+result+"");});},function(exn){$("#error").
text(error);});Note:RxhasexceptionhandlingoperatorssuchasCatch,Finally,OnErrorResumeNextandRetrywhichallowtakingacompositionalapproachtoerrorhandling.
Wewon'telaborateontherichexceptionhandlingoperatorspresentinRxandwillkeepthingssimplebyusinganOnErrorhandlerpassedtoSubscribe.
7.
Onegeneralissuewithdistributedprogrammingweshouldcalloutisthepotentialforout-of-orderarrivalofresponses.
Inthenextexercise,we'llcomposetheuserinputfromthebridgedHTMLDOMinputelementwithwebservicecalls,somultiplerequestsmayberunningatthesametime.
Forexample,iftheusertypes"reac",waitsonesecond(forThrottletoforwardthestringtoitsobservers),thenproceedswithtyping"reactive"andwaitsanothersecond,bothwebservicecallswillbeinflight.
Theresponsetothesecondcallmayarrivebeforethecalltothefirstonedoes.
Thisisnottoofar-fetchedevenforthissimplesample:asthere'llbemorewordsstartingwith"reac"thanwith"reactive",thefirstrequestwillbemorenetwork-intensivethanthesecondone.
Wecanmimicthissituationquiteeasilybystartingacoupleofwebservicerequestsfor"incrementalstrings"andobservetheorderanswerscomebackin:varinput="reactive";varmakeRequest=function(len){varreq=input.
substring(0,len);searchWikipedia(req).
Subscribe(function(results){$("#results").
append(""+reqresults.
length+"");},function(exn){$("#error").
text(error);});};for(varlen=3;lenInordertomakethingsmoreconcrete,we'verenamedtheinputcontrolto"searchInput".
Furthermore,recallwe'veimportedthefollowingthreelibrariesinthesectionoftheHTMLpage:WikipediaLookupInthepreviousexercise,webuiltafunctionwrappingtheWikipediaservice.
Thiscodeisdefinedinanotherblock:functionsearchWikipedia(term){return$.
ajaxAsObservable({url:"http://en.
wikipedia.
org/w/api.
php",dataType:"jsonp",data:{action:"opensearch",search:term,format:"json"}}).
Select(function(d){returnd.
data[1];});}Finally,wedefinedawaytotametheuserinputusingRx,resultinginathrottledobservablesequenceboundtothe"searchInput"element.
Thiswashookedupinthedocument'sreadyeventfromthesameblockasshownabove:$(document).
ready(function(){varterms=$("#searchInput").
toObservable("keyup").
Select(function(event){return$(event.
target).
val();}).
Throttle(250);//Timetocomposestuffhere.
.
.
});Inhere,we'veomittedsubscriptionstoeitherofthosesourcessincewe'renowgoingtocomposeboth.
2.
Atthispointwegottwothings:anobservablesequenceofinputstringsandafunctionthattakesastringandproducesanobservablesequencecontaininganarrayofcorrespondingwordsfromWikipedia.
HowdowegluethosetwotogetherTheanswertothisquestionliesintheincrediblypowerfulSelectManyoperator.
ShowinghowitworkscanbestbedonebyusingRxfor.
NETwherethemethodsignaturehasrichtypes:Ignorethegenericnatureofthetypes,andeventhetypesthemselvesinthecontextofRxJS.
WhatmattersisthatwegotalltheingredientsneededforcompositionfedtoSelectMany.
Inourcase,thesourceargumentwillproducetermsenteredbytheuser.
Ourwebservicewrapperfunctionactsasthesecondargument,mappingthoseinputtermsontoanobservablesequencecontainingtheWikipediaresults.
WhattheSelectManyoperatorcandousingthoseinputsisbindthemtogether.
Mostreaderswillbefamiliarwiththisoperatorinapossiblyunconsciousmanner.
Everytimeyouquerysomedatabasetraversingarelationshipbetweentables,you'rereallydealingwithSelectMany.
Forexample,assumeyouwanttogetallthesuppliersacrossalltheproductsinastore.
StartingfromasequenceofProductobjectsandawaytomapaProductontoaSupplier(e.
g.
afunctionretrievingaProduct'sSuppliedByproperty),theoperatorcangiveusaflattenedlistofSupplierobjectsacrossallProductobjects.
Thefollowingfigureillustratesthisbindingoperationforour"Wikipediacomplete"application:3.
Let'sturnSelectManyintomotionnow.
Giventhe"terms"observablesequenceandthemappingfunction,wecangoaheadandaddthefollowingcodetothedocumentreadyeventhandler:varsearchObservable=terms.
SelectMany(function(term){returnsearchWikipedia(term);});Foreverytermenteredbytheuser,acallwillbemadetosearchWikipedia.
Theresultsofthatcallwillendupintheresulting"searchObservable"sequencethatwillbeboundtotheUIinthenextstep.
Thisisallweneedtodotocomposethetwoasynchronouscomputations,theresultstillbeingasynchronousbyitselfaswell.
HereinlaysthepowerofRx:retainingtheasynchronousnatureofcomputationsinthepresenceofrichcompositionoperators(or"combinators").
Note:ThiscodecanbesimplifiedabitsincesearchWikipediaisafunctionbyitselfalready.
Hence,there'snoneedtowrapthefunctioninyetanotherfunction.
However,lotsofpeopletendtowritecodethiswaysincetheyforgetaboutthefirstclasscitizenshipoffunctionsinlanguageslikeJavaScript.
Reducingtheclutterforpassingafunctionisrootedintheprincipleofetareductioninlambdacalculus:varsearchObservable=terms.
SelectMany(searchWikipedia);Note:InRxfor.
NET,onecanuseC#'squeryexpressionsyntaxwheretheuseofSelectManyistriggeredbythepresenceofmultiplefromclauses.
ThisgetstranslatedintooneoftheSelectManyoverloads:varres=fromtermintermsfromwordsinsearchWikipedia(term)selectwords;Background:SelectManyisoneofthemostpowerfuloperatorsofRx.
Itspowerisrootedintheunderlyingtheoryofmonads,leveragedbyLINQingeneral.
Whilemonadsmaysoundlikeascarydisease,theyreallyarearathersimpleconcept.
Intheworldofmonads,theSelectManyoperationiscalledbind.
Itspurposeinlifeistotakeanobject"inthemonad",applyafunctiontoittoendupwithanotherobject"inthemonad".
It'sbasicallysomekindoffunctionapplicationonsteroids,threadingaconcernthroughoutthecomputation.
4.
ThenextstepistobindtheresultstotheUI.
Inordertoreceivetheresults,wegottosubscribetheresultingobservablesequence.
Nomatterhowcomplexthecompositioniswe'retalkingabout,theresultisstilllazy.
Inthiscase,theSelectManyusehasreturnedanobservablesequencewithWikipediaentriesinarrays.
Allofthiswon'tdoanythingtillwemakeacalltoSubscribe.
Fromthatpointon,throttleduserinputwilltriggerwebservicecallswhoseresultsaresenttotheobserverpassedtoSubscribe:searchObservable.
Subscribe(function(results){$("#results").
empty();$.
each(results,function(_,result){$("#results").
append(""+result+"");});},function(exn){$("#error").
text(error);});Inthispieceofcode,wereceivethewordsintheOnNexthandler.
Afterclearingtheresultsbulletlist,weusethejQueryeachiterationfunctiontopopulatethelistwiththevaluesreceived.
Incaseanerrorresultsfromcallingtheservice,itwillbepropagatedtothesearchObservable'sOnErrorchannelwhichwesubscribetoaswelltonotifytheuserthroughtheerrorelement.
Thescreenshotbelowshowsafragmentoftheoutput:5.
Oneproblemislurkingaroundthecorner,waitingtocomeandgetus:out-of-orderarrival.
TounderstandwhythiscanhappenatallinthecontextofourcompositionusingSelectMany,weneedtoelaborateontheworkingoftheoperator.
WhenevertheSelectManyoperatorreceivesaninputonitssource,itevaluatestheselectorfunctiontoobtainanobservablesequencethatwillprovidedatafortheoperator'soutput.
Essentiallyitflattensalloftheobservablesequencesobtainedinthismannerintooneflatresultingsequence.
Forexample,iftheusertypes"react"andidlesoutforatleastonesecond,SelectManyreceivesthestringfromtheThrottleoperatorprecedingit.
Executionoftheselectorfunction-searchWikipedia–causesawebservicecalltobestarted.
Whilethiscallisinflight,theusermayenter"reactive"whichmayenduptriggeringanotherwebservicecallinasimilarmanner(onesecondthrottledelay,applicationoftheselectorfunction).
Atthatpoint,twoparallelcallsarehappening,whichcouldprovideresultsout-of-ordercomparedtotheinput.
Thefigurebelowillustratestheissuethatcanariseduetothisbehavior:Sincethrottlingaddsa250milliseconddelay,youhavetobeabit(un)luckytohitthisissue.
However,ifitremainsunfixed,it'dlikelycomeandgetyouthefirsttimeyoudemotheapplicationtoyourboss.
Sincewedon'twantthistohappentous,weneedtocanceloutexistingwebservicerequestsassoonastheuserentersanewterm,indicatingthere'snofurtherinterestintheprevioussearchterm.
Whatwewanttoachieveisillustratedinthefigureonthenextpage.
Theessenceofthefixis"crossingout"or"muting"in-flightrequestswhenanewoneisreceived.
Thisisillustratedastheredcrossonthelineforthefirstwebservicecall.
Note:AtsomepointinthedesignofRx,therewasaspecialSelectManyoperatorwithcancellationbehavior:wheneveranobjectwasreceivedonthesource,theprevious(ifany)innerobservablewasunsubscribedbeforecallingtheselectorfunctiontogetanewinnerobservable.
Asyoumayexpect,thisledtonumerousdebateswhichofthetwobehaviorswasdesired.
HavingtwodifferentoperatorflavorsforSelectManywasnotagoodthingforvariousreasons.
Foronething,onlyoneflavorcouldbetiedtotheC#andVisualBasicLINQsyntax.
Theultimatesolutionwastodecouplethenotionofcancellationfromtheconceptof"monadicbind".
Asaresult,newcancellationoperatorsarose,whichdohavetheiruseinalotofotherscenariostoo.
Awin-winsituation!
6.
TherealizationofthiscancellationbehaviorcanbeachievedusingasingleoperatorcalledSwitchwhosebehaviorisillustratedinthediagrambelow:Givenasequenceofsequences(yes,that'snotatypo)ithopsfromonesequencetoanotherastheycomein.
Whenthetopmostobservable"outer"sequenceproducesanewobservable"inner"sequence,anexistinginnersequencesubscriptionisdisposedandthenewlyreceivedsequenceissubscribedto.
ResultsproducedbythecurrentinnersequencearepropagatedtotheSwitchoperator'soutput.
Useofthisoperatorinourscenarioproceedsasfollows.
InsteadofusingSelectMany,we'llmapuserinputonwebservicerequestsusingasimpleSelectoperatoruse.
Sinceeverywebservicerequestreturnsanobservablesequencewithanarrayofresultsasthepayloadtheresultofthisprojectionisan"observableofobservables".
Ifweweretohavetypes,we'dhaveanIObservable>here.
ApplyingSwitchoverthisnestedsequencecausesthebehaviordescribedabove.
Foreveryrequestsubmittedbytheuser,thewebserviceiscontacted.
Ifanewrequestismade,Switchcancelsouttheexistingone'ssubscriptionandhopstothenewone:varsearchObservable=terms.
Select(searchWikipedia);.
Switch();Withthisfixinplace,out-of-orderarrivalshouldnotbeabletocomeandgetus.
Note:Alternativefixestothistypicalasynchronousprogrammingproblemexist.
IntheHOLforthe.
NETversionofRx,wepresenttheuseoftheTakeUntiloperatorthatactsasavalveonanobservablesequence.
Withthisdifferentapproach,wetakeresultsfromthecurrentwebservicecalluntilanotheruserrequestismade.
Conclusion:CompositionofmultipleasynchronousdatasourcesisoneofthemainstrengthsofRx.
InthisexercisewelookedattheSelectManyoperatorthatallows"binding"onesourcetoanother.
Morespecially,weturneduserentriesofterms(originatingfromDOMevents)–intoasynchronouswebservicecalls(broughttoRxusingajaxAsObservable).
Todealwitherrorsandout-of-orderarrivalonlyaminimalportionofthecodehadtobetweaked.
Whilewedidn'tmentionoperatorsotherthanSelectManyandSwitchthatdealwithmultiplesources,sufficeittosaythatawholebunchofthoseexistawaitingyourfurtherexploration.
Exercise8–TestabilityandmockingmadeeasyObjective:Testingasynchronouscodeisahardproblem.
Representingasynchronousdatasourcesasfirst-classobjectsimplementingacommon"interface",Rxisinauniquepositiontohelptosimplifythistaskaswell.
We'lllearnhoweasyitistomockobservablesequencestocreatesolidtests.
1.
Eachobservablesequencecanberegardedasatestableunit.
Inourdictionarysuggestsample,twoessentialsequencesarebeingused.
Oneistheuserinputsequence;theotherisitscompositionwiththewebservice.
Sinceallofthosearefirst-classobjects,wecansimplyswapthemoutforasequence"mock".
OneparticularlyusefuloperatorinRxJSisFromArraywhichtakesaJavaScriptarray.
It'sapull-to-pushadapterthatenumerates(pulls)thegivenarrayandexposesitasanobservablesequencethatnotifies(pushes)itsobserversofthearray'selements.
Asanexample,replacetheinputsequenceforanarray-basedmockobservableasshownbelow://varterms=$("#searchInput").
toObservable("keyup")//.
Select(function(event){return$(event.
target).
val();})//.
Throttle(250);varinput=Rx.
Observable.
FromArray(["reac","reactive","bing"]);Nowwhenwetrytoruntheapplication,ourwebservicewillbefed"reac","reactive"and"bing"virtuallyatthesametimesincethere'snodelaybetweentheelementsintheinput.
IftheSwitchoperatordoesitsout-of-orderpreventingjobcorrectly,onlytheresultsforthe"bing"requestshouldappearonthescreen.
It'saworthyexperimenttotakeouttheSwitchoperatorfromthepreviousexerciseandobserveresponsescomingback.
Withabitofluck,you'llseetheissuecroppingupduetothemultiplerequestsbeingfiredrightafteroneanother.
2.
Thankstothevarioustime-basedoperators,wecanprovidemorerealisticinput.
Onethingwecoulddotomockuserinputinamorefaithfulwayistousetime-basedoperatorstomimictyping.
GenerateWithTimeisasuitablecandidateforournextexperiment.
Let'sgenerateasequenceofincrementalsubstringsforagiventerm,withrandomdelaysbetweenthem.
varinput="reactive";varterms=Rx.
Observable.
GenerateWithTime(1,function(len){returnlen<=input.
length;},function(len){returnlen+1;},function(len){returninput.
substring(0,len);},function(_){returnMath.
random()*1000;}).
Do(function(term){$("#searchInput").
val(term);});Hereweusearandomnumbergeneratortosimulatetypingspeedvariations(suchthatThrottlewillsometimesletasubstringthrough).
Beforewethrottlethesequence,weusetheside-effectingDooperatortoputthesubstringintheinputelementtoreallysimulatetheusertypinginavisualmanner.
3.
Similarly,wecouldmockthewebservicebyreplacingthesearchWikipediafunction.
Obviouslyonewillhavetocomeupwithsomeoutputtogowiththeinputterm,maybefromalocaldictionaryorbasedonsomedummyoutputgeneration.
Belowisasamplewebservicemock:functionsearchWikipedia(term){//return$.
ajaxAsObservable(//{url:"http://en.
wikipedia.
org/w/api.
php",//dataType:"jsonp",//data:{action:"opensearch",//search:term,//format:"json",//limit:100//}//})//.
Select(function(d){returnd.
data[1];});varresult=newArray();for(vari=0;irandom()*50;i++)result[i]=term+i;returnRx.
Observable.
Return(result).
Delay(Math.
random()*10000);}Inthiscode,wefirstgenerateanarrayof0to50resultssimplybasedontheservice"term"inputwithanumberconcatenated.
Forexample,given"rea",wemayget,"rea0","rea1","rea2"-back.
Sincetheservicecontractistoreturnanobservableofsuchanarray,weuseRx.
Observable.
Returntocreateasingle-elementobservablesequencewiththegeneratedarray.
Finally,weusetheDelayoperatorthat'sdefinedforobservablesequencestoaddarandom1-to-10seconddelayinsendingbacktheresponse.
Runningthesampleagainwithouttheout-of-orderprevention,itshouldbeplaineasytohittheout-of-orderarrivalissuewehavedescribedinmuchdetailbefore:varsearchObservable=terms.
SelectMany(searchWikipedia);//.
Switch();Theresultisindeeddevastating.
Clearly,theoutputshownbelowisn'tright.
Whenseeingthesimulatedtypingflyby,youmayseeresultsfor"reactive"comingbackandthengettingoverwrittenbytheresponsesofsomepriorcall.
Assumesuchanissuehasbeenuncoveredinyourcode;it'sincrediblysimpletocreateatestcaseforitusingmockslikethose.
Conclusion:Thefirstclassobjectnatureofobservablesequencesmakesiteasytoreplacethem,contrasttovariousasynchronoustechnologieslikeDOMevents.
Thisallowsforsmoothtestingofasynchronousprogramsusingmockinputsequences,e.
g.
basedonarraysturnedintoobservablesequenceswithFromArray.

Buyvm:VPS/块存储补货1Gbps不限流量/$2起/月

BuyVM测评,BuyVM怎么样?BuyVM好不好?BuyVM,2010年成立的国外老牌稳定商家,Frantech Solutions旗下,主要提供基于KVM的VPS服务器,数据中心有拉斯维加斯、纽约、卢森堡,付费可选强大的DDOS防护(月付3美金),特色是1Gbps不限流量,稳定商家,而且卢森堡不限版权。1G或以上内存可以安装Windows 2012 64bit,无需任何费用,所有型号包括免费的...

ReliableSite怎么样,月付$95美国洛杉矶独立服务器

ReliableSite怎么样?ReliableSite好不好。ReliableSite是一家成立于2006年的老牌美国商家,主要经营美国独立服务器租赁,数据中心位于:洛杉矶、迈阿密、纽约,带宽1Gbps起步,花19美元/月即可升级到10Gbps带宽,月流量150T足够各种业务场景使用,且免费提供20Gbps DDoS防护。当前商家有几款大硬盘美国独服,地点位于美国洛杉矶或纽约机房,机器配置很具有...

TTcloud:日本独立服务器促销活动,价格$70/月起,季付送10Mbps带宽

ttcloud怎么样?ttcloud是一家海外服务器厂商,运营服务器已经有10年时间,公司注册地址在香港地区,业务范围包括服务器托管,机柜托管,独立服务器等在内的多种服务。我们后台工单支持英文和中文服务。TTcloud最近推出了新上架的日本独立服务器促销活动,价格 $70/月起,季付送10Mbps带宽。也可以跟进客户的需求进行各种DIY定制。点击进入:ttcloud官方网站地址TTcloud拥有自...

jqueryeach为你推荐
在B2B的登录首页下方,点击账号密码查询连接腾讯周鸿祎尊敬的浪潮英信服务器用户:functionscss敬请参阅最后一页特别声明甘肃省政府采购支持ipadtracerouteping命令和traceroute(tracert )在功能上的区别有哪些?ipadwifiIpad怎么用移动无线上网itunes备份itunes备份是什么
山东虚拟主机 花生壳域名 短域名 便宜vps ion 空间打开慢 好看的桌面背景图片 免费ddos防火墙 最好看的qq空间 卡巴斯基永久免费版 qq数据库下载 52测评网 共享主机 qq对话框 申请网页 万网主机 SmartAXMT800 酷锐 asp简介 连连支付 更多