Understand JavaScript Callback Functions and Use Them-回调函数
2016-08-29 00:00
591 查看
(LearnJavaScriptHigher-orderFunctions,akaCallbackFunctions)
InJavaScript,functionsarefirst-classobjects;thatis,functionsareofthetypeObjectandtheycanbeusedinafirst-classmannerlikeanyotherobject(String,Array,Number,etc.)sincetheyareinfactobjectsthemselves.Theycanbe“storedinvariables,passedasargumentstofunctions,createdwithinfunctions,andreturnedfromfunctions”1.OurCareerPathsandCoursesWebsiteIsNowLive
Learn.ModernDeveloperLaunched
Ourfirstcohortisinsession:97%ofourfirstcohortontargettograduate.Enrollinthesecondcohort.CareerPath1:JavaScriptDeveloperandCareerPath3:ModernFrontendDeveloperusuallyfillupquickly.TableofContents
ReceiveUpdates
Becausefunctionsarefirst-classobjects,wecanpassafunctionasanargumentinanotherfunctionandlaterexecutethatpassed-infunctionorevenreturnittobeexecutedlater.ThisistheessenceofusingcallbackfunctionsinJavaScript.IntherestofthisarticlewewilllearneverythingaboutJavaScriptcallbackfunctions.CallbackfunctionsareprobablythemostwidelyusedfunctionalprogrammingtechniqueinJavaScript,andyoucanfindtheminjustabouteverypieceofJavaScriptandjQuerycode,yettheyremainmysterioustomanyJavaScriptdevelopers.Themysterywillbenomore,bythetimeyoufinishreadingthisarticle.Callbackfunctionsarederivedfromaprogrammingparadigmknownasfunctionalprogramming.Atafundamentallevel,functionalprogrammingspecifiestheuseoffunctionsasarguments.Functionalprogrammingwas—andstillis,thoughtoamuchlesserextenttoday—seenasanesoterictechniqueofspeciallytrained,masterprogrammers.
Fortunately,thetechniquesoffunctionalprogramminghavebeenelucidatedsothatmeremortalslikeyouandmecanunderstandandusethemwithease.Oneofthechieftechniquesinfunctionalprogramminghappenstobecallbackfunctions.Asyouwillreadshortly,implementingcallbackfunctionsisaseasyaspassingregularvariablesasarguments.ThistechniqueissosimplethatIwonderwhyitismostlycoveredinadvancedJavaScripttopics.
WhatisaCallbackorHigher-orderFunction?
Acallbackfunction,alsoknownasahigher-orderfunction,isafunctionthatispassedtoanotherfunction(let’scallthisotherfunction“otherFunction”)asaparameter,andthecallbackfunctioniscalled(orexecuted)insidetheotherFunction.Acallbackfunctionisessentiallyapattern(anestablishedsolutiontoacommonproblem),andtherefore,theuseofacallbackfunctionisalsoknownasacallbackpattern.ConsiderthiscommonuseofacallbackfunctioninjQuery:
//Notethattheitemintheclickmethod'sparameterisafunction,notavariable. | |
//Theitemisacallbackfunction | |
$("#btn_1").click(function(){ | |
alert("Btn1Clicked"); | |
}); |
RuminateonthisotherclassicexampleofcallbackfunctionsinbasicJavaScript:
varfriends=["Mike","Stacy","Andy","Rick"]; | |
| |
friends.forEach(function(eachName,index){ | |
console.log(index+1+"."+eachName);//1.Mike,2.Stacy,3.Andy,4.Rick | |
}); |
Sofarwehavepassedanonymousfunctionsasaparametertootherfunctionsormethods.Letsnowunderstandhowcallbacksworkbeforewelookatmoreconcreteexamplesandstartmakingourowncallbackfunctions.
HowCallbackFunctionsWork?
Wecanpassfunctionsaroundlikevariablesandreturntheminfunctionsandusetheminotherfunctions.Whenwepassacallbackfunctionasanargumenttoanotherfunction,weareonlypassingthefunctiondefinition.Wearenotexecutingthefunctionintheparameter.Inotherwords,wearen’tpassingthefunctionwiththetrailingpairofexecutingparenthesis()likewedowhenweareexecutingafunction.Andsincethecontainingfunctionhasthecallbackfunctioninitsparameterasafunctiondefinition,itcanexecutethecallbackanytime.
Notethatthecallbackfunctionisnotexecutedimmediately.Itis“calledback”(hencethename)atsomespecifiedpointinsidethecontainingfunction’sbody.So,eventhoughthefirstjQueryexamplelookedlikethis:
//Theanonymousfunctionisnotbeingexecutedthereintheparameter. | |
//Theitemisacallbackfunction | |
$("#btn_1").click(function(){ | |
alert("Btn1Clicked"); | |
}); |
CallbackFunctionsAreClosures
Whenwepassacallbackfunctionasanargumenttoanotherfunction,thecallbackisexecutedatsomepointinsidethecontainingfunction’sbodyjustasifthecallbackweredefinedinthecontainingfunction.Thismeansthecallbackisaclosure.Readmypost,
BasicPrincipleswhenImplementingCallbackFunctions
Whileuncomplicated,callbackfunctionshaveafewnoteworthyprinciplesweshouldbefamiliarwithwhenimplementingthem.UseNamedORAnonymousFunctionsasCallbacks
IntheearlierjQueryandforEachexamples,weusedanonymousfunctionsthatweredefinedintheparameterofthecontainingfunction.Thatisoneofthecommonpatternsforusingcallbackfunctions.Anotherpopularpatternistodeclareanamedfunctionandpassthenameofthatfunctiontotheparameter.Considerthis:
//globalvariable | |
varallUserData=[]; | |
| |
//genericlogStufffunctionthatprintstoconsole | |
functionlogStuff(userData){ | |
if(typeofuserData==="string") | |
{ | |
console.log(userData); | |
} | |
elseif(typeofuserData==="object") | |
{ | |
for(variteminuserData){ | |
console.log(item+":"+userData[item]); | |
} | |
| |
} | |
| |
} | |
| |
//Afunctionthattakestwoparameters,thelastoneacallbackfunction | |
functiongetInput(options,callback){ | |
allUserData.push(options); | |
callback(options); | |
| |
} | |
| |
//WhenwecallthegetInputfunction,wepasslogStuffasaparameter. | |
//SologStuffwillbethefunctionthatwillcalledback(orexecuted)insidethegetInputfunction | |
getInput({name:"Rich",speciality:"JavaScript"},logStuff); | |
//name:Rich | |
//speciality:JavaScript |
Sincethecallbackfunctionisjustanormalfunctionwhenitisexecuted,wecanpassparameterstoit.Wecanpassanyofthecontainingfunction’sproperties(orglobalproperties)asparameterstothecallbackfunction.Intheprecedingexample,wepassoptionsasaparametertothecallbackfunction.Let’spassaglobalvariableandalocalvariable:
//Globalvariable | |
vargeneralLastName="Clinton"; | |
| |
functiongetInput(options,callback){ | |
allUserData.push(options); | |
//PasstheglobalvariablegeneralLastNametothecallbackfunction | |
callback(generalLastName,options); | |
} |
Itisalwayswisetocheckthatthecallbackfunctionpassedintheparameterisindeedafunctionbeforecallingit.Also,itisgoodpracticetomakethecallbackfunctionoptional.
Let’srefactorthegetInputfunctionfromthepreviousexampletoensurethesechecksareinplace.
functiongetInput(options,callback){ | |
allUserData.push(options); | |
| |
//Makesurethecallbackisafunction | |
if(typeofcallback==="function"){ | |
//Callit,sincewehaveconfirmeditiscallable | |
callback(options); | |
} | |
} |
ProblemWhenUsingMethodsWithThethisObjectasCallbacks
Whenthecallbackfunctionisamethodthatusesthethisobject,wehavetomodifyhowweexecutethecallbackfunctiontopreservethethisobjectcontext.Orelsethethisobjectwilleitherpointtotheglobalwindowobject(inthebrowser),ifcallbackwaspassedtoaglobalfunction.Oritwillpointtotheobjectofthecontainingmethod.
Let’sexplorethisincode:
//Defineanobjectwithsomepropertiesandamethod | |
//Wewilllaterpassthemethodasacallbackfunctiontoanotherfunction | |
varclientData={ | |
id:094545, | |
fullName:"NotSet", | |
//setUserNameisamethodontheclientDataobject | |
setUserName:function(firstName,lastName){ | |
//thisreferstothefullNamepropertyinthisobject | |
this.fullName=firstName+""+lastName; | |
} | |
} | |
| |
functiongetUserInput(firstName,lastName,callback){ | |
//DootherstufftovalidatefirstName/lastNamehere | |
| |
//Nowsavethenames | |
callback(firstName,lastName); | |
} |
getUserInput("Barack","Obama",clientData.setUserName); | |
| |
console.log(clientData.fullName);//NotSet | |
| |
//ThefullNamepropertywasinitializedonthewindowobject | |
console.log(window.fullName);//BarackObama |
WecanfixtheprecedingproblembyusingtheCallorApplyfunction(wewilldiscusstheseinafullblogpostlater).Fornow,knowthateveryfunctioninJavaScripthastwomethods:CallandApply.Andthesemethodsareusedtosetthethisobjectinsidethefunctionandtopassargumentstothefunctions.
Calltakesthevaluetobeusedasthethisobjectinsidethefunctionasthefirstparameter,andtheremainingargumentstobepassedtothefunctionarepassedindividually(separatedbycommasofcourse).TheApplyfunction’sfirstparameterisalsothevaluetobeusedasthethisobjectinsidethefunction,whilethelastparameterisanarrayofvalues(ortheargumentsobject)topasstothefunction.
Thissoundscomplex,butletsseehoweasyitistouseApplyorCall.Tofixtheprobleminthepreviousexample,wewillusetheApplyfunctionthus:
//Notethatwehaveaddedanextraparameterforthecallbackobject,called"callbackObj" | |
functiongetUserInput(firstName,lastName,callback,callbackObj){ | |
//Dootherstufftovalidatenamehere | |
| |
//TheuseoftheApplyfunctionbelowwillsetthethisobjecttobecallbackObj | |
callback.apply(callbackObj,[firstName,lastName]); | |
} | |
|
//WepasstheclientData.setUserNamemethodandtheclientDataobjectasparameters.TheclientDataobjectwillbeusedbytheApplyfunctiontosetthethisobject | |
getUserInput("Barack","Obama",clientData.setUserName,clientData); | |
| |
//thefullNamepropertyontheclientDatawascorrectlyset | |
console.log(clientData.fullName);//BarackObama |
MultipleCallbackFunctionsAllowed
Wecanpassmorethanonecallbackfunctionsintotheparameterofafunction,justlikewecanpassmorethanonevariable.HereisaclassicexamplewithjQuery’sAJAXfunction:
functionsuccessCallback(){ | |
//Dostuffbeforesend | |
} | |
| |
functionsuccessCallback(){ | |
//Dostuffifsuccessmessagereceived | |
} | |
| |
functioncompleteCallback(){ | |
//Dostuffuponcompletion | |
} | |
| |
functionerrorCallback(){ | |
//Dostuffiferrorreceived | |
} | |
| |
$.ajax({ | |
url:"http://fiddle.jshell.net/favicon.png", | |
success:successCallback, | |
complete:completeCallback, | |
error:errorCallback | |
| |
}); |
“CallbackHell”ProblemAndSolution
Inasynchronouscodeexecution,whichissimplyexecutionofcodeinanyorder,sometimesitiscommontohavenumerouslevelsofcallbackfunctionstotheextentthatyouhavecodethatlookslikethefollowing.Themessycodebelowiscalledcallbackhellbecauseofthedifficultyoffollowingthecodeduetothemanycallbacks.Itookthisexamplefromthenode-mongodb-native,aMongoDBdriverforNode.js.[2].Theexamplecodebelowisjustfordemonstration:varp_client=newDb('integration_tests_20',newServer("127.0.0.1",27017,{}),{'pk':CustomPKFactory}); | |
p_client.open(function(err,p_client){ | |
p_client.dropDatabase(function(err,done){ | |
p_client.createCollection('test_custom_key',function(err,collection){ | |
collection.insert({'a':1},function(err,docs){ | |
collection.find({'_id':newObjectID("aaaaaaaaaaaa")},function(err,cursor){ | |
cursor.toArray(function(err,items){ | |
test.assertEquals(1,items.length); | |
| |
//Let'sclosethedb | |
p_client.close(); | |
}); | |
}); | |
}); | |
}); | |
}); | |
}); |
Nameyourfunctionsanddeclarethemandpassjustthenameofthefunctionasthecallback,insteadofdefiningananonymousfunctionintheparameterofthemainfunction.
Modularity:Separateyourcodeintomodules,soyoucanexportasectionofcodethatdoesaparticularjob.Thenyoucanimportthatmoduleintoyourlargerapplication.
MakeYourOwnCallbackFunctions
Nowthatyoucompletely(Ithinkyoudo;ifnotitisaquickreread:))understandeverythingaboutJavaScriptcallbackfunctionsandyouhaveseenthatusingcallbackfunctionsarerathersimpleyetpowerful,youshouldlookatyourowncodeforopportunitiestousecallbackfunctions,fortheywillallowyouto:Donotrepeatcode(DRY—DoNotRepeatYourself)
Implementbetterabstractionwhereyoucanhavemoregenericfunctionsthatareversatile(canhandleallsortsoffunctionalities)
Havebettermaintainability
Havemorereadablecode
Havemorespecializedfunctions.
Itisrathereasytomakeyourowncallbackfunctions.Inthefollowingexample,Icouldhavecreatedonefunctiontodoallthework:retrievetheuserdata,createagenericpoemwiththedata,andgreettheuser.Thiswouldhavebeenamessyfunctionwithmuchif/elsestatementsand,evenstill,itwouldhavebeenverylimitedandincapableofcarryingoutotherfunctionalitiestheapplicationmightneedwiththeuserdata.
Instead,Ilefttheimplementationforaddedfunctionalityuptothecallbackfunctions,sothatthemainfunctionthatretrievestheuserdatacanperformvirtuallyanytaskwiththeuserdatabysimplypassingtheuser’sfullnameandgenderasparameterstothecallbackfunctionandthenexecutingthecallbackfunction.
Inshort,thegetUserInputfunctionisversatile:itcanexecuteallsortsofcallbackfunctionswithmyriadoffunctionalities.
//First,setupthegenericpoemcreatorfunction;itwillbethecallbackfunctioninthegetUserInputfunctionbelow. | |
functiongenericPoemMaker(name,gender){ | |
console.log(name+"isfinerthanfinewine."); | |
console.log("Altruisticandnobleforthemoderntime."); | |
console.log("Alwaysadmirablyadornedwiththelateststyle."); | |
console.log("A"+gender+"ofunfortunatetragedieswhostillmanagesaperpetualsmile"); | |
} | |
| |
//Thecallback,whichisthelastitemintheparameter,willbeourgenericPoemMakerfunctionwedefinedabove. | |
functiongetUserInput(firstName,lastName,gender,callback){ | |
varfullName=firstName+""+lastName; | |
| |
//Makesurethecallbackisafunction | |
if(typeofcallback==="function"){ | |
//Executethecallbackfunctionandpasstheparameterstoit | |
callback(fullName,gender); | |
} | |
} |
getUserInput("Michael","Fassbender","Man",genericPoemMaker); | |
//Output | |
/*MichaelFassbenderisfinerthanfinewine. | |
Altruisticandnobleforthemoderntime. | |
Alwaysadmirablyadornedwiththelateststyle. | |
AManofunfortunatetragedieswhostillmanagesaperpetualsmile. | |
*/ |
functiongreetUser(customerName,sex){ | |
varsalutation=sex&&sex==="Man"?"Mr.":"Ms."; | |
console.log("Hello,"+salutation+""+customerName); | |
} | |
| |
//PassthegreetUserfunctionasacallbacktogetUserInput | |
getUserInput("Bill","Gates","Man",greetUser); | |
| |
//Andthisistheoutput | |
Hello,Mr.BillGates |
Asyousee,callbackfunctionsaffordmuchversatility.Andeventhoughtheprecedingexampleisrelativelysimple,imaginehowmuchworkyoucansaveyourselfandhowwellabstractedyourcodewillbeifyoustartusingcallbackfunctions.Goforit.Doitinthemonings;doitintheevenings;doitwhenyouaredown;doitwhenyouarek
NotethefollowingwayswefrequentlyusecallbackfunctionsinJavaScript,especiallyinmodernwebapplicationdevelopment,inlibraries,andinframeworks:
Forasynchronousexecution(suchasreadingfiles,andmakingHTTPrequests)
InEventListeners/Handlers
InsetTimeoutandsetIntervalmethods
ForGeneralization:codeconciseness
FinalWords
JavaScriptcallbackfunctionsarewonderfulandpowerfultouseandtheyprovidegreatbenefitstoyourwebapplicationsandcode.Youshouldusethemwhentheneedarises;lookforwaystorefactoryourcodeforAbstraction,Maintainability,andReadabilitywithcallbackfunctions.Seeyounexttime,andremembertokeepcomingbackbecauseJavaScriptIsSexy.comhasmuchtoteachyouandyouhavemuchtolearn.
Notes
byStoyanStefanov(Sep28,2010)
Postedin:
相关文章推荐
- Understand JavaScript Callback Functions and Use Them
- [Javascript] Understand Function Composition By Building Compose and ComposeAll Utility Functions
- Top JavaScript Frameworks, Libraries & Tools and When to Use Them
- JavaScript closure: difference between call and apply as well as use them to create inheritance
- Describe 3 kernel functions and when to use which of them
- 20060315-Quick tip: Use the functions true and false
- 【JavaScript】Understanding callback functions in Javascript
- JQuery Callback and Functions
- Callback Functions in JavaScript
- [Javascript] Some very simple functions for Word and Excel handling
- UNDERSTANDING CALLBACK FUNCTIONS IN JAVASCRIPT
- Why Prepared Statements are important and how to use them "properly"
- FolderSync :The various features and how to use them
- JavaScript Errors and How to Fix Them
- Use gitk to understand git – merge and rebase
- Callback and Promise in Javascript
- [JavaScript] Defining and Invoking Functions
- First use cursor and initially understand it
- [JavaScript] Use `+` opertor to add one string and one number
- 理解javascript中的回调函数(callback)