I think they work well as long as they're reasonably specific, reposting the famous HN Dropbox comment also works pretty well on HN. Other redditisms don't fare so well, and for that I'm thankful.
Not arguing for or againts something, this is just something I wanted to say.
Framing matters here, the API of Promises in js maps almost nicely to the common monad API of bind (then), join (implicitely done by the runtime whenever possible), and return (Promise.resolve). Learning any monads is unlikely to be harder than this as these operations are indeed quite intuitive.
What is often referenced with learning monads is instead Learning All the Monads, sometimes adding a touch of Learning All the Monad Transformers and interactions of different monads. This intrinsecally needs to be done in a generic/parametric way and is way harder.
To my knowledge Haskell is the most mainstream[1] language only typed language that allows expressing the categorical definition, most other type systems are significantly more limited in how much maths/category theory they can express and often focus on specialized functionality with practical implication like Rust's ownership system or Typescript's Capitalize<StringType> that only exists to allow nicer typing of some common API desings[2]
[1] most mainstream typed language at least, you can implement Monad is JS/TS but the language cannot express it the same way C cannor express generics even if you can manually implement them in it.
Monads and Promises are not comparable.
The IO Monad fulfills a similar role to Promises and people learning Haskell grasp them immediately.
Haskell also has an equivalent of the async/await syntax called the do notation which makes things nice and easy to read.
You don't really need to understand monads to understand the IO monad or Promises.
If you want to understand monads, look at their signature, mainly the bind operator and try to implement something with it. A logger, a list, the IO monad itself.
It's not that hard, it's just that nobody bothers playing with it and just try to learn the theory without experimenting with monads in code. After a few tests you'll build an intuition for it and you'll get why they call them programmable semicolons.
Monads is just one of the abstractions that can be used to implement IO in Haskell btw, it was just the authors flexing their category theory that got us in this situation.
At the same time, one of the reasons I learned Haskell is that it had a reputation for being hard and, boy, am I grateful for it.
Basically, think of a monad as a Promise. Rather, a Promise is more-or-less an example of a monad. (Yes, I know that due to some technicalities it isn't, but it behaves like one for the purposes of this comment)
Mapping a monad is equivalent to Promise#then - if there's a value in the monad, then it calls the function you passed to map and returns the result wrapped in a monad. If there isn't a value in the monad, then it returns itself.
For example, with the Maybe monad, if you have x = Maybe.Some(y), then x.map(f) = Maybe.some(f(y)). If you have x = Maybe.None, then x.map(f) = Maybe.None.
With Promises, if you have x = Promise.resolve(1234), then x.then(x => x * 2) = Promise.resolve(1234 * 2). If you have x = Promise.reject(new Error('abcd')), then x.then(x => x * 2) = Promise.reject(new Error('abcd')).
The IO monad is extremely similar to Promises - you call an IO function and get an IO monad as a result, then map that monad in much the same way you'd then a promise.
The problem with monads is they are only really practical in a language with built-in syntactic sugar to cover the boilerplate. In any other language you will surely go "whats the point?" because using monads will invariably turn simple code into a convoluted mess for no benefit. Promises on the other hand will seem immediately useful if you have tried writing async code in an ad-hoc manner. Promises solve a problem.
Mathematicians and Haskelites tend to explain things by giving their definition. (Imaging a Haskelite explaining how to write "hello world" in C: First you need a "main" function. A function is a process or a relation that associates each element x of a set X, the domain of the function, to a single element y of another set Y (possibly the same set), the codomain of the function. etc etc )
But most other programmers prefer to understand things by understanding the problem they solve. It is quite obvious what problems Promises solve, but in the context of JavaScript, monads does not solve any real world problem. That makes them hard to grasp for a programmer, even though the concept is simple.
Monads are a particular pattern for method chaining or function composition.
Here is an example of some JavaScript code which use regular method chaining:
[1,2,3].map(a => a + 1).filter(b => b != 3)
This code results in the array [2,4].
Similar code following the monad pattern would look like this:
And the result is the same. But obviously the monadic version is more convoluted and harder to read. But if there was some syntactic sugar which covered the boilerplate, then the monadic version might be bearable!
The "power" of the monadic pattern is that the operations can be chained or nested in a more flexible way. For example here the operations are nested, but the result is the same:
This does have some nice properties, since operations can be chained or nested together to composites which have the same type as a single operation. The question is if the benefit outweighs the cost in code complexity.
The monad pattern is purely concerned about how operations are chained together structurally, it is not about what they does or what types are involved. In this example the type is Array<T>, but it could be any parameterized type.
Monads can be used anywhere a sequence of operations is stringed together. (But that doesn't mean you would want to.)
Monad imho is deliberately badly explained to preserve the mystique and the smugness of the cognoscenti. I found this book invaluable for translating the field into something that makes sense: https://alvinalexander.com/scala/functional-programming-simp...
I know dozens of people who tried to understand monads (including myself) and maybe 3 of them succeeded (I do not consider myself one of them).