Skip to content

Instantly share code, notes, and snippets.

@sliminality
Last active December 18, 2025 00:56
Show Gist options
  • Select an option

  • Save sliminality/fcc5cb01a1cd24551f26a9110d802b63 to your computer and use it in GitHub Desktop.

Select an option

Save sliminality/fcc5cb01a1cd24551f26a9110d802b63 to your computer and use it in GitHub Desktop.
nested promises example
///Problems with nested Promises: very easy to **forget to return**, thus breaking the Promise chain\\- No arrow functions yet, must explicitly return each time//- People are accustomed to line starting with **doC(…)** from CPS. Nesting Promises is **structurally similar** to CPS, but error handling only works at the top level!
doA()
.then(function (a) {
doB() ///_⚠ Missing __**return**_
.then(function (b) {
doE()
doC(a, b) ///_⚠ Missing __**return**_
})
})
.then(function (d) { window.alert("Success: " + d) })
.catch(function (err) { window.alert("Got an error:" + err) }) ///⚠ Won't catch any errors from **doB**, **doE**, **doC**
function doA() { return Promise.resolve("a") }
function doB() { return Promise.resolve("b") }
function doC(a, b) { return Promise.resolve(`${a}${b}`) }
// function doD(c) { return c === "ab" ? Promise.resolve("d") : Promise.reject(new Error("Wrong input: expected ab, got " + c)) }
function doE() { return Promise.resolve() }
///Problem: suppose you want to structure some async code like this.\\\\` // ┌──────▶ D// │// │ b c//A ─────▶ B ─────▶ C ─────▶ …//│ ▲//└─────────────────┘// a`\\\\Using continuation-passing style ("callback hell") and pre-ES2015 syntax for authenticity, it would look something like this:\\\\\\\\\\
doA(function (err, a) {
// if (err) { return handleError(err) }
doB(function (err, b) {
// if (err) { return handleError(err) }
doE(function (err) {
// if (err) { return handleError(err) }
})
doC(a, b, function (err, c) {
// if (err) { return handleError(err) }
doD(c, function (err, d) {
window.alert("Success: " + d)
})
})
})
})
function handleError(err) {
window.alert("Caught error:", err)
}
function doA(next) { next(null, "a") }
function doB(next) { next(null, "b") }
function doC(a, b, next) { next(null, a + b) }
function doE(next) { next(null) }
function doD(c, next) {
if (c === "ab") {
next(null, "d")
} else {
next(new Error("Wrong input: expected ab, got " + c), null)
}
}
///How do we write this code using early-style Promises?
function doA() { return Promise.resolve("a") }
function doB() { return Promise.resolve("b") }
function doC(a, b) { return Promise.resolve(`${a}${b}`) }
// function doD(c) { return c === "ab" ? Promise.resolve("d") : Promise.reject(new Error("Wrong input: expected ab, got " + c)) }
function doE() { return Promise.resolve() }
doA()
.then(function (a) {
return Promise.all([a, doB()]) ///Need to use **Promise.all** and return a pair to thread **a** into the next call
})
.then(function (ab) {
doE()
return doC(ab[0], ab[1]) ///Now unpack the tuple **a**, **b** to pass arguments to **doC**
})
// .then(doD)
.then(function (d) { window.alert("Success: " + d) })
.catch(function (err) { window.alert("Got an error:" + err) })
doA() ///This is a far cry from the fluent syntax in the advertisement for Promises! (need to thread parameters, etc.)\\\\So, let's try rewriting this with **nested Promises**:
.then(function (a) {
return doB()
.then(function (b) {
doE()
return doC(a, b) ///Can shadow **a** and **b** directly without threading
})
})
// .then(doD)
.then(function (d) { window.alert("Success: " + d) })
.catch(function (err) { window.alert("Got an error:" + err) })
///Even if you **return** correctly, there are pitfalls
doA()
.then(function (a) {
return doB() ///_✔ Correctly returned_
.then(function (b) {
doE() ///_(Don't return, just send off this call)_
return doC(a, b) ///_✔ Correctly returned_
})
})
.then(function (d) { window.alert("Success: " + d) })
.catch(function (err) { window.alert("Got an error:" + err) }) ///_✔ \_Now, this \_will_ catch errors from **doA**, **doB**, **doC**
function doA() { return Promise.resolve("a") }
function doB() { return Promise.resolve("b") }
function doC(a, b) { return Promise.resolve(`${a}${b}`) }
// function doD(c) { return c === "ab" ? Promise.resolve("d") : Promise.reject(new Error("Wrong input: expected ab, got " + c)) }
function doE() { throw new Error("E failed") }
///Can try adding inner .**catch** clause…
doA()
.then(function (a) {
return doB()
.then(function (b) {
// doE() //.catch(function (err) { window.alert("Inner error:" + err) }) ///\_✔ \_Catches **doE** if **doE** rejects a Promise, but _not_ if **doE** throws exception
return doC(a, b)
})
})
.then(function (d) { window.alert("Success: " + d) })
.catch(function (err) { window.alert("Outer error:" + err) }) ///\_✔ \_Catches **doA**, **doB**, **doC** (exception or rejected Promise), \\Catches **doE **if thrown exception, _not_ if rejected Promise
function doA() { return Promise.resolve("a") }
function doB() { return Promise.resolve("b") }
// function doC(a, b) { return Promise.resolve(`${a}${b}`) }
// function doC(a, b) { return Promise.reject(new Error("c")) }
function doC(a, b) { throw new Error("c") }
function doE() { return Promise.reject(new Error("E failed")) }
// function doE() { throw new Error("E failed") }
doA() ///Where to add **catch** for **doC(…)**?
.then(function (a) {
return doB()
.then(function (b) {
doE()
return doC(a, b) // ?? ///Here?
}) // ?? ///Here?
}) // ?? ///Here?
.then(function (d) { window.alert("Success: " + d) })
.catch(function (err) { window.alert("Outer error:" + err) })
///Promises were _kind of_ like synchronous exception handling, but not quite.\\- Exceptions would coalesce through subsequent chained calls, but not up and down the call stack//- Wanted you to be able to use Promises, sync code interchangeably, so allowed **.then(x => 1)** and would Promisify **1**, but this meant it wasn't clear when exceptions were thrown/caught.
doA()
.then(function (a) {
return doB()
.then(function (b) {
doE()
return doC(a, b)
})
})
.then(function (d) { window.alert("Success: " + d) })
.catch(function (err) { window.alert("Got an error:" + err) })
function doA() { return Promise.resolve("a") }
function doB() { return Promise.resolve("b") }
function doC(a, b) { return Promise.resolve(`${a}${b}`) }
// function doD(c) { return c === "ab" ? Promise.resolve("d") : Promise.reject(new Error("Wrong input: expected ab, got " + c)) }
function doE() { throw new Error("E: Hi") }
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment