For starters, your code is so full of serious syntax errors that in some places it's not even close to valid JavaScript. This is my best guess reconstruction:
(async (e) => {
await doSomething()
while (!done) {
({ done, value }) = await reader.read()
}
promise
.then(goodA, badA)
.then(goodB, badB)
.catch(err => console.log(err))
.finally(() => {
using stack = new DisposableStack()
stack.defer(() => console.log('done.'))
})
})()
But more importantly, this isn't even close to anything a reasonable JS dev would ever write.
1. It's not typical to mix await and while(!done), I can't imagine what library actually needs this. You usually use one or the other, and it's almost always just await:
await doSomething()
const value = await readFully(reader)
2. If you're already inside an Async IIFE, you don't need promise chains. Just await the stuff as needed, unless promise chains make the code shorter and cleaner, e.g.:
const json = await fetch(url).then(r => r.json())
3. Well designed JS libraries don't usually stack promise handlers like the {good,bad}{A,B} functions you implied. You usually just write code and have a top level exception handler:
using stack = new DisposableStack()
stack.defer(() => console.log('done.'))
try {
const goodA = await promise
const goodB = await goodA
const goodC = await goodB
return goodC
}
catch(e) {
myLogErr(e)
}
// finally isn't needed, that's the whole point of DisposableStack
4. We don't usually need AIIFEs anymore, so the outer layer can just go away.
note about that await block: "await" will await the _entire_ return, so if "promise" returns another promise ("goodA") which in turn also returns a promise ("goodB"), which in turn returns _another_ promise that ends up resolving as the non-promise value "goodC", then "await promise" just... gets you "goodC", directly.
The "example code" (if we can call it that) just used goodA and goodB because it tried to make things look crazy, by writing complete nonsense: none of that is necessary, we can just use a single, awaiting return:
Done. "await" waits until whatever it's working with is no longer a promise, automatically either resolving the entire chain, or if the chain throws, moving us over to the exception catching part of our code.
That is a matter of opinion. JavaScript allows you to use either convention at your preference. Personally, I feel my code looks much, much cleaner without semicolons. I also use whitespace liberally.
For the longest time, I used them just in case it would otherwise cause a bug. But TypeScript fully takes this into account and checks for all these scenarios.
1. It's not typical to mix await and while(!done), I can't imagine what library actually needs this. You usually use one or the other, and it's almost always just await:
2. If you're already inside an Async IIFE, you don't need promise chains. Just await the stuff as needed, unless promise chains make the code shorter and cleaner, e.g.: 3. Well designed JS libraries don't usually stack promise handlers like the {good,bad}{A,B} functions you implied. You usually just write code and have a top level exception handler: 4. We don't usually need AIIFEs anymore, so the outer layer can just go away.