You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
JS functions being first-class citizens allows for Continuation Passing Style logic
In other words, call a function when a operation is complete
This is a callback
Concurrency
constfs=require('fs')functiongetInode(path,cb){fs.stat(path,(err,stats)=>{if(err){returncb(err)}cb(null,stats.ino,path)})}getInode(process.cwd(),(err,inode,path)=>{if(err){console.error("Couldn't do it chum",inode)return}console.log('inode for %s is %d',path,inode)})
In Node, the callback convention strictlyerror first... then values
A function that accepts a callback generally takes the callback last
Like promises, async is a project flow-control choice
Error handling
Asynchronous error handling patterns have already been demonstrated
Error first callbacks
Manually propagate errors up through parent callbacks
async - handled error propagation
Promises built-in error catching (including sync errors) and propagation
Error handling
Error categories
Developer errors
human error
should always crash the process immediately and noisily
Operation errors
system failure
failures resulting from bad user input
should be caught and handled if possible
Error handling
Went to throw
Developer error
Unresolvable Operational errors that lead to a fatal status
When to try/catch
When an API throws outside of the above scenarios
Other possible edge scenarios, like syntax detection
Error handling
Went to never throw
If there's any chance that the error could be operational
e.g. parsing or serializing based on user input
When to never try/catch
Never try to catch an error that could occur asynchronously, it won't be caught
Error handling
try/catch causes significant deoptimizations
always isolate a try/catch in its own function
Alternatives to the throw - try - catch pattern
Return an Error object and check return value
Return an object with a value and error properties
Code Reuse
Avoid inheritance as a master pattern
Problems with class inheritance
creates parent/child object taxonomies as a side-effect
the fragile base class problem
tight coupling
inflexible hierarchy
duplication by necessity
gorilla-banaca problem
Code Reuse
Especially avoid deeply nested structures
Deep hierarchies create maintenance nightmares
Deep hierarchies create debugging issues
Deep hierarchies create performance issues, both intrinsically and as an emergent property
Code Reuse
Class Inheritance in JavaScript is an emulation - It's prototype inheritance with class semantics
This leads to fundamental misunderstandings
And a variety of nefarious fail-modes at the mesh points
e.g. like what happens when forgetting new
Code Reuse
Prefer a functional approach
Prefer to store state with closure scope
Prefer composition
Code Reuse
functioncreateWorld(name='Cronenberg World'){constspecies=newSet()return{ name, createBeing, createWalker, createBiped, getExistingSpecies }functiongetExistingSpecies(){returnArray.from(species)}functioncreateBeing({name, type ='being'}={}){species.add(type)return{ name, reproduce }functionreproduce(name){returnObject.assign({},this,{name})}}functioncreateWalker({name, type ='walker', legs =4, speed =10}={}){conststepsPerSec=1000/speedletsteps=0letintvreturnObject.assign({},createBeing({name, type}),{walk: ()=>{intv=intv||setInterval(()=>steps+=legs,stepsPerSec)returntype+' is walking'},stop: ()=>{clearInterval(intv)intv=nullreturntype+' walked '+steps+' steps'}})}functioncreateBiped({name, type ='biped', step =2, speed =5}={}){returncreateWalker({name, type, step, speed})}}
Code Reuse
Avoid ES6 classes
because they encourage class inheritance
and it's still just prototype inheritance
Use constructors internally
in property access hot paths
when a large amount (1000's) of instances will be created
as a conscious decision to help identify memory leaks whilst profiling
keep structures flat however
Code Reuse
functioncreateWorld(name='Cronenberg World'){constspecies=newSet()functiongetExistingSpecies(){returnArray.from(species)}functioncreateBeing({name, type ='being'}={}){species.add(type)return{ name, reproduce }functionreproduce(name){return{__proto__: this, name}}}// NOTE - optimization/profilingfunctionWalker(name,type,legs,stepsPerSec){this._intvl=nullthis._steps=0this.name=namethis.type=typethis._legs=legsthis._stepsPerSec=stepsPerSec}Walker.prototype=createBeing({type: 'walker'})Walker,prototype.walk=function(){this._intv=this._intv||setInterval(()=>this._steps+=this._legs,this.stepsPerSec)returnthis.type+' is walking'}Walker.prototype.stop=function(){clearInterval(this._intv)this._intv=nullreturnthis.type+' walked '+this._steps+' steps'}functioncreateWalker({name, type ='walker', legs =4, speed =10}={}){returnnewWalker(name,type,legs,1000/speed)}functioncreateBiped({name, type ='biped', step =2, speed =5}={}){returncreateWalker({name, type, step, speed})}return{ name, createBeing, createWalker, createBiped, getExistingSpecies }}
ES5
From Node v0.10 to v6 ES5 is supported - use it
The Array map, reduce, forEach, etc. methods support the FP paradigm, use them
If you find you need speed, go straight to procedural loops
Lodash is only suitable if the plan is to use advancd functional methods, and
the entire team understands this advanced approach
use strict is generally good practice, and helps to prevent certain bugs
Object.create, Object.defineProperty, ObjectDefineProperties whilst verbose can be useful in certain
situations - avoid creativity with getters and setters
Object.freeze et. al. and bind are just too expensive
ES6
If project constraints allow, it's perfectly okay to use modern features if they support a functional approach
Transpilation was an intermediate inconvenience, it's better to move away from build steps if we can
And avoid transpiling to older versions, behaviours can differ
ES6
use const for module assignments (you don't want those bindings overwritten!)
use const unless there's an explicit need to reassign
const optimizes
const can stop reassignment programmer errors
ES6
constVERSION=1constNAME='Tiny Rick'constusage= '
-----------------------------------------
Welcome to ${NAME} version ${VERSION}
-----------------------------------------
template strings are awesome
multiline
easy to use other quote marks
interpolation is cleaner - also allows expressions
tag functions are weird...
ES6
parameter defaults are awesome:
name = 'bob', say = 'nothing') =>
name + ' says' + say`