Thinking about this from a Haskell perspective isn't very helpful, because what you
actually get is simply an "IO String", and it doesn't behave much like any Node option. If you want concurrency, you use forkIO, or one of the many things built on top of it (async, concurrent pipes, whatever), and if you don't, you don't. Regardless, the IO type is always "async" in the way that Node types define it (or, alternatively, transcends the way Node uses the term entirely); there is no distinction between an IO String that is simply 'return "a string"' and an IO String that actually hits a web server API to figure out what other web server to hit for a file which will then be fed to an API which will return a string... that's still just an "IO String".
Haskell does have a thing from the promise tradition, but it's a different definition of "promise" than JS is using here, and it manifests in Control.Concurrent.MVar. (And, again, let me be clear, if that doesn't look like what the JS is doing here, correct. The wikipedia page is helpful. [1] AFAIK, JS is not wrong in its usage of the term promise, but there's a lot of other academic study behind the term and there's a lot of different variants in that history.)
You don't need a promises monad, IO already does it, combined with the scheduler.
[1]: http://en.wikipedia.org/wiki/Futures_and_promises