Last active
January 14, 2021 12:02
-
-
Save mk0y/9604452373a60593f3773a9e4b82ac27 to your computer and use it in GitHub Desktop.
Code example from article https://vertrical.com/blog/practical-function-composition-in-javascript
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
const asyncCompose = (...fns) => input => | |
fns.reduceRight( | |
(chain, fn) => chain.then(fn), | |
Promise.resolve(input) | |
) | |
// import { authService } from 'authservice' | |
const authService = null | |
const getUser = async ({ auth = authService, ...dependencies }) => { | |
try { | |
const user = await auth() | |
return { username: user.username, ...dependencies } | |
} catch (e) { | |
// you could also throw exception if you want to fail fast, meaning if flow isn't possible without auth service | |
return { error: e, username: 'default', ...dependencies } | |
} | |
} | |
// import { dbService } from 'dbservice' | |
const dbService = null | |
const withDB = async ({ db = dbService, ...dependencies }) => { | |
// managing db is usually much more complex | |
// this also can be wrapped with try-catch, depends on your needs | |
const dbInstance = db.currentInstance() | |
if (!dbInstance) { | |
throw new Error('no database :(') | |
} | |
return { coll: dbInstance, ...dependencies } | |
} | |
// dependencies is optional since filterSketchesBy only needs sketches | |
// this function is future proof since it passes through dependencies | |
const findSketches = async ({ coll, ...dependencies }) => { | |
const { username, sketch: sketchSearch } = dependencies | |
const sketchesByName = await coll.findByName(username, sketchSearch) | |
return { sketches: sketchesByName, ...dependencies } | |
} | |
const filterSketchesBy = ({ sketches, filter, ...dependencies }) => { | |
if (!sketches.length) { | |
return { sketches, ...dependencies } | |
} | |
const filtered = sketches.filter(sketch => sketch.name.toLowerCase().includes(filter.toLowerCase())) | |
return { sketches: filtered, ...dependencies } | |
} | |
// [ 1, 2, 3, 4, 5, 6, 7, 8, 9 ] => [ [ 1, 2, 3, 4 ], [ 5, 6, 7, 8 ], [ 9 ] ] | |
const paginateSketches = ({ sketches, perPage = 4, ...dependencies }) => { | |
if (!sketches.length) { | |
return { sketches, ...dependencies } | |
} | |
const paginated = sketches.reduce( | |
(acc, cur, i) => { | |
const ii = i + 1; | |
if (i % 4 === 0) { acc.push([]); } | |
acc[Math.ceil(ii / perPage) - 1].push(cur); | |
return acc; | |
}, | |
[] | |
) | |
return { sketches: paginated, ...dependencies } | |
} | |
const getAllUserSketches = asyncCompose(findSketches, withDB, getUser) | |
const withAdditionalFilter = asyncCompose(filterSketchesBy, getAllUserSketches) | |
const paginatedSketches = asyncCompose(paginateSketches, withAdditionalFilter) | |
const sketchesSuccess = [{ | |
name: 'jiberish' | |
}, { | |
name: 'mock this' | |
}, { | |
name: 'mock that' | |
}, { | |
name: 'little house' | |
}, { | |
name: 'awesome logo' | |
}] | |
// happy path, get all, no filter | |
getAllUserSketches({ | |
auth: () => Promise.resolve({ username: 'bojack' }), | |
db: { currentInstance: () => ({ findByName: () => Promise.resolve(sketchesSuccess) }) }, | |
}).then(console.log) | |
// with filter | |
withAdditionalFilter({ | |
auth: () => Promise.resolve({ username: 'juju876' }), | |
db: { currentInstance: () => ({ findByName: () => Promise.resolve(sketchesSuccess) }) }, | |
filter: 'mock', | |
}).then(console.log) | |
// paginated | |
paginatedSketches({ | |
auth: () => Promise.resolve({ username: 'zhu1234567' }), | |
db: { currentInstance: () => ({ findByName: () => Promise.resolve(sketchesSuccess) }) }, | |
filter: 'mock', | |
}).then(console.log) | |
// auth service is allowing further flow | |
// find error in result.error | |
paginatedSketches({ | |
auth: () => Promise.reject('auth service is down'), | |
db: { currentInstance: () => ({ findByName: () => Promise.resolve(sketchesSuccess) }) }, | |
filter: 'mock', | |
}).then(console.log) | |
// db exits early, result is only "no database :(" | |
paginatedSketches({ | |
auth: () => Promise.resolve({ username: 'shmoopie' }), | |
db: { currentInstance: () => null }, | |
filter: 'mock', | |
}).then(console.log).catch(({ message }) => console.error(message)) | |
// db exits early, result is only "timed out..." | |
paginatedSketches({ | |
auth: () => Promise.resolve({ username: 'hoopie' }), | |
db: { currentInstance: () => ({ findByName: () => Promise.reject(new Error('timed out...')) }) }, | |
filter: 'mock', | |
}).then(console.log).catch(({ message }) => console.error(message)) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment