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.

ShockHosting($4.99/月),东京机房 可享受五折优惠,下单赠送10美金

ShockHosting商家在前面文章中有介绍过几次。ShockHosting商家成立于2013年的美国主机商,目前主要提供虚拟主机、VPS主机、独立服务器和域名注册等综合IDC业务,现有美国洛杉矶、新泽西、芝加哥、达拉斯、荷兰阿姆斯特丹、英国和澳大利亚悉尼七大数据中心。这次有新增日本东京机房。而且同时有推出5折优惠促销,而且即刻使用支付宝下单的话还可获赠10美金的账户信用额度,折扣相比之前的常规...

青云互联:洛杉矶CN2弹性云限时七折,Cera机房三网CN2gia回程,13.3元/月起

青云互联怎么样?青云互联是一家成立于2020年6月份的主机服务商,致力于为用户提供高性价比稳定快速的主机托管服务,目前提供有美国免费主机、香港主机、香港服务器、美国云服务器,让您的网站高速、稳定运行。目前,美国洛杉矶cn2弹性云限时七折,美国cera机房三网CN2gia回程 13.3元/月起,可选Windows/可自定义配置。点击进入:青云互联官网青云互联优惠码:七折优惠码:dVRKp2tP (续...

Cloudxtiny:£1.5/月,KVM-512MB/100GB/英国机房

Cloudxtiny是一家来自英国的主机商,提供VPS和独立服务器租用,在英国肯特自营数据中心,自己的硬件和网络(AS207059)。商家VPS主机基于KVM架构,开设在英国肯特机房,为了庆祝2021年欧洲杯决赛英格兰对意大利,商家为全场VPS主机提供50%的折扣直到7月31日,优惠后最低套餐每月1.5英镑起。我们对这场比赛有点偏见,但希望这是一场史诗般的决赛!下面列出几款主机套餐配置信息。CPU...

jqueryeach为你推荐
"2018年中文图书第5期新书通报",,,,,Max163程序微信5computationgraph支持ipad支持ipad支持ipadcss3圆角怎样用css实现圆角矩形?win10关闭445端口win10家庭版怎么禁用445端口iphonewifi苹果手机怎么wi-fi共享
快速域名备案 hostigation 美国翻墙 Dedicated kddi 好看的桌面背景图 工作站服务器 最好的qq空间 ca187 空间购买 万网主机管理 网站加速软件 免费ftp 国外在线代理服务器 国外免费云空间 卡巴斯基试用版下载 免备案jsp空间 cdn加速技术 web服务器 cpu使用率过高怎么办 更多