Observations on Promises

Promises are a cast-able Container

  • If you have a 🍔, you also have a Promise for a 🍔.
  • Any value, 🍔, can be cast up to a Promise<🍔> via Promise.resolve(🍔).
  • So, Promise.resolve(🍔) → Promise<🍔>.
  • Importantly, though, if a promise for a hamburger is given to Promise.resolve, that self-same promise is returned.
  • Thus Promise.resolve(Promise<🍔>) → Promise<🍔>.
function Rate (food) {
return chew(food).then(ponderQuality)
}
// consumers of Rate have to materialize food to rate it.
getFoodSomehow().then(food => Rate(food))
function Rate (food) {
return Promise.resolve(food).then(chew).then(ponderQuality)
}
// consumers of Rate don't need to
// materialize their food promise:
Rate(getFoodSomehow())
// but if they had a hamburger on
// hand, we wouldn't be opposed to
// rating it, either:
Rate(existingFood)
const getFood = obtainFood()
const getRating = Rate(getFood)
return Blog(getFood, getRating)
// vs.
obtainFood().then(food => {
return Rate(food).then(rating => {
return Blog(food, rating)
})
})

Promises Represent the Dependence Graph of Values

                   getFood -----> getRating
\_____ ____/
\ /
\ /
Y

getBlogArticle
var food
obtainFood().then(_food => {
food = _food // yoink it out into enclosing scope
return Rate(food)
}).then(rating => {
return Blog(food, rating)
})
// alternatively:
obtainFood().then(food => {
return Promise.all([food, Rate(food)])
}).spread((food, rating)=> {
return Blog(food, rating)
})
  • If it’s storing an intermediate result in the enclosing scope, that variable is an implicit state machine that may or may not hold the expected value at any given point in the execution of the code. It’s a small danger, but an avoidable one.
  • Alternatively, if the value is piggybacking along, it becomes difficult to insert a step between those two steps — the value has to be piggybacked along all intermediate steps.
  • The dependence graph isn’t faithfully represented, but that might not be clear to later programmers — “are these values truly dependent on one another, or was the last programmer taking a shortcut?”
/*
setupDBState
| \_______
↓ \
getHTTPResponse |
|\____________ |
| \|
↓ ↓
checkHTTPStatus fetchFromDB
| |
| ↓
| checkDBState
\_____ ____/
\ /
Y

doneWithTest
*/
tape('test that adding a user works', assert => {
const setupDBState = Promise.props({
user: User.save(),
pkg: Package.save()
})
const getHTTPResponse = setupDBState.then(objs => {
return Promise.promisify(request.post)({
url: `${API_URL}/package/${objs.pkg.name}/add`,
body: { "user": objs.user.name },
json: true
})
})
const checkHTTPStatus = getHTTPResponse.get(0).then(resp => {
assert.equal(resp.statusCode, 200)
})
// we depend on "getHTTPResponse" having been made, and
// "setupDBState"'s result, so use `.return` to represent this.

const fetchFromDB = getHTTPResponse.return(
setupDBState
).then(objs => {
return UserPackage.find({
user_id: objs.user.id,
package_id: objs.pkg.id
})
})
const checkDBState = fetchFromDB.then(userPkg => {
assert.equal(userPkg.perms, 'read')
})
return Promise.join(
checkHTTPStatus,
checkDBState
).return(null).then(assert.end).catch(assert.end)
})
// go from one value to another:
const upperCase = somePromise.then(value => value.toUpperCase())
// go from several values to another:
const originalPlusNew = Promise.join(
somePromise,
upperCase
).spread((original, upperCase) => original + upperCase)
// depend on the order of a promise, not the
// value it contains. I'm calling this out
// explicitly with ".return(null)".

const result = originalPlusNew.return(null).then(() => {
return 42
})
function anExample (getUser) {
getUser = Promise.resolve(getUser)
const getName = getUser.then(user => {
return user.fullName
})
const checkId = getUser.then(user => {
assert.ok(user.id)
})
const capitalizeName = getName.then(name => name.toUpperCase())
return capitalizeName
}
// because capitalizeName does not depend on
//
checkId, checkID can enter states that the
// program at large cannot respond to!

--

--

--

there's no problem javascript can't solve or change into a bigger, more interesting problem | https://github.com/chrisdickinson

Love podcasts or audiobooks? Learn on the go with our new app.

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store
Chris Dickinson

Chris Dickinson

there's no problem javascript can't solve or change into a bigger, more interesting problem | https://github.com/chrisdickinson

More from Medium

Breakdown of the Asynchronous Promise: Fetch() Method

Typescript for the rescue

Why functions are called First-Class Citizens in JavaScript

SOLID: What is? Why this? How use?